427 lines
14 KiB
C++
427 lines
14 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "WebRenderImageHost.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "mozilla/ScopeExit.h"
|
|
#include "mozilla/gfx/gfxVars.h"
|
|
#include "mozilla/layers/AsyncImagePipelineManager.h"
|
|
#include "mozilla/layers/CompositorThread.h"
|
|
#include "mozilla/layers/CompositorVsyncScheduler.h" // for CompositorVsyncScheduler
|
|
#include "mozilla/layers/KnowsCompositor.h"
|
|
#include "mozilla/layers/RemoteTextureHostWrapper.h"
|
|
#include "mozilla/layers/RemoteTextureMap.h"
|
|
#include "mozilla/layers/WebRenderBridgeParent.h"
|
|
#include "mozilla/layers/WebRenderTextureHost.h"
|
|
#include "mozilla/ProfilerMarkers.h"
|
|
#include "mozilla/StaticPrefs_gfx.h"
|
|
#include "mozilla/StaticPrefs_webgl.h"
|
|
#include "nsAString.h"
|
|
#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
|
|
#include "nsPrintfCString.h" // for nsPrintfCString
|
|
#include "nsString.h" // for nsAutoCString
|
|
|
|
#if XP_WIN
|
|
# include "mozilla/layers/GpuProcessD3D11TextureMap.h"
|
|
# include "mozilla/layers/TextureHostWrapperD3D11.h"
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
|
|
using namespace gfx;
|
|
|
|
namespace layers {
|
|
|
|
class ISurfaceAllocator;
|
|
|
|
WebRenderImageHost::WebRenderImageHost(const TextureInfo& aTextureInfo)
|
|
: CompositableHost(aTextureInfo), mCurrentAsyncImageManager(nullptr) {}
|
|
|
|
WebRenderImageHost::~WebRenderImageHost() {
|
|
MOZ_ASSERT(mPendingRemoteTextureWrappers.empty());
|
|
MOZ_ASSERT(mWrBridges.empty());
|
|
}
|
|
|
|
void WebRenderImageHost::OnReleased() {
|
|
ImageComposite::ClearImages();
|
|
if (!mPendingRemoteTextureWrappers.empty()) {
|
|
mPendingRemoteTextureWrappers.clear();
|
|
}
|
|
SetCurrentTextureHost(nullptr);
|
|
}
|
|
|
|
void WebRenderImageHost::UseTextureHost(
|
|
const nsTArray<TimedTexture>& aTextures) {
|
|
CompositableHost::UseTextureHost(aTextures);
|
|
MOZ_ASSERT(aTextures.Length() >= 1);
|
|
|
|
if (!mPendingRemoteTextureWrappers.empty()) {
|
|
mPendingRemoteTextureWrappers.clear();
|
|
}
|
|
|
|
if (mCurrentTextureHost &&
|
|
mCurrentTextureHost->AsRemoteTextureHostWrapper()) {
|
|
mCurrentTextureHost = nullptr;
|
|
}
|
|
|
|
nsTArray<TimedImage> newImages;
|
|
|
|
for (uint32_t i = 0; i < aTextures.Length(); ++i) {
|
|
const TimedTexture& t = aTextures[i];
|
|
MOZ_ASSERT(t.mTexture);
|
|
if (i + 1 < aTextures.Length() && t.mProducerID == mLastProducerID &&
|
|
t.mFrameID < mLastFrameID) {
|
|
// Ignore frames before a frame that we already composited. We don't
|
|
// ever want to display these frames. This could be important if
|
|
// the frame producer adjusts timestamps (e.g. to track the audio clock)
|
|
// and the new frame times are earlier.
|
|
continue;
|
|
}
|
|
TimedImage& img = *newImages.AppendElement();
|
|
img.mTextureHost = t.mTexture;
|
|
img.mTimeStamp = t.mTimeStamp;
|
|
img.mPictureRect = t.mPictureRect;
|
|
img.mFrameID = t.mFrameID;
|
|
img.mProducerID = t.mProducerID;
|
|
img.mTextureHost->SetCropRect(img.mPictureRect);
|
|
}
|
|
|
|
SetImages(std::move(newImages));
|
|
|
|
if (GetAsyncRef()) {
|
|
for (const auto& it : mWrBridges) {
|
|
RefPtr<WebRenderBridgeParent> wrBridge = it.second->WrBridge();
|
|
if (wrBridge && wrBridge->CompositorScheduler()) {
|
|
wrBridge->CompositorScheduler()->ScheduleComposition(
|
|
wr::RenderReasons::ASYNC_IMAGE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Video producers generally send replacement images with the same frameID but
|
|
// slightly different timestamps in order to sync with the audio clock. This
|
|
// means that any CompositeUntil() call we made in Composite() may no longer
|
|
// guarantee that we'll composite until the next frame is ready. Fix that
|
|
// here.
|
|
if (mLastFrameID >= 0 && !mWrBridges.empty()) {
|
|
for (const auto& img : Images()) {
|
|
bool frameComesAfter =
|
|
img.mFrameID > mLastFrameID || img.mProducerID != mLastProducerID;
|
|
if (frameComesAfter && !img.mTimeStamp.IsNull()) {
|
|
for (const auto& it : mWrBridges) {
|
|
RefPtr<WebRenderBridgeParent> wrBridge = it.second->WrBridge();
|
|
if (wrBridge) {
|
|
wrBridge->AsyncImageManager()->CompositeUntil(
|
|
img.mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WebRenderImageHost::PushPendingRemoteTexture(
|
|
const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId,
|
|
const base::ProcessId aForPid, const gfx::IntSize aSize,
|
|
const TextureFlags aFlags) {
|
|
// Ensure aOwnerId is the same as RemoteTextureOwnerId of pending
|
|
// RemoteTextures.
|
|
if (!mPendingRemoteTextureWrappers.empty()) {
|
|
auto* wrapper =
|
|
mPendingRemoteTextureWrappers.front()->AsRemoteTextureHostWrapper();
|
|
MOZ_ASSERT(wrapper);
|
|
if (wrapper->mOwnerId != aOwnerId || wrapper->mForPid != aForPid) {
|
|
// Clear when RemoteTextureOwner is different.
|
|
mPendingRemoteTextureWrappers.clear();
|
|
mWaitingReadyCallback = false;
|
|
mWaitForRemoteTextureOwner = true;
|
|
}
|
|
}
|
|
|
|
// Check if waiting for remote texture owner is allowed.
|
|
if (!(aFlags & TextureFlags::WAIT_FOR_REMOTE_TEXTURE_OWNER)) {
|
|
mWaitForRemoteTextureOwner = false;
|
|
}
|
|
|
|
RefPtr<TextureHost> texture =
|
|
RemoteTextureMap::Get()->GetOrCreateRemoteTextureHostWrapper(
|
|
aTextureId, aOwnerId, aForPid, aSize, aFlags);
|
|
MOZ_ASSERT(texture);
|
|
mPendingRemoteTextureWrappers.push_back(
|
|
CompositableTextureHostRef(texture.get()));
|
|
}
|
|
|
|
void WebRenderImageHost::UseRemoteTexture() {
|
|
if (mPendingRemoteTextureWrappers.empty()) {
|
|
return;
|
|
}
|
|
|
|
const bool useReadyCallback = bool(GetAsyncRef());
|
|
CompositableTextureHostRef texture;
|
|
|
|
if (useReadyCallback) {
|
|
if (mWaitingReadyCallback) {
|
|
return;
|
|
}
|
|
MOZ_ASSERT(!mWaitingReadyCallback);
|
|
|
|
auto readyCallback = [self = RefPtr<WebRenderImageHost>(this)](
|
|
const RemoteTextureInfo aInfo) {
|
|
RefPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
|
|
"WebRenderImageHost::UseRemoteTexture",
|
|
[self = std::move(self), aInfo]() {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
|
|
if (self->mPendingRemoteTextureWrappers.empty()) {
|
|
return;
|
|
}
|
|
|
|
auto* wrapper = self->mPendingRemoteTextureWrappers.front()
|
|
->AsRemoteTextureHostWrapper();
|
|
MOZ_ASSERT(wrapper);
|
|
if (wrapper->mOwnerId != aInfo.mOwnerId ||
|
|
wrapper->mForPid != aInfo.mForPid) {
|
|
// obsoleted callback
|
|
return;
|
|
}
|
|
|
|
self->mWaitingReadyCallback = false;
|
|
self->UseRemoteTexture();
|
|
});
|
|
|
|
CompositorThread()->Dispatch(runnable.forget());
|
|
};
|
|
|
|
// Check which of the pending remote textures is the most recent and ready.
|
|
while (!mPendingRemoteTextureWrappers.empty()) {
|
|
auto* wrapper =
|
|
mPendingRemoteTextureWrappers.front()->AsRemoteTextureHostWrapper();
|
|
|
|
if (mWaitForRemoteTextureOwner) {
|
|
// XXX remove sync wait
|
|
RemoteTextureMap::Get()->WaitForRemoteTextureOwner(wrapper);
|
|
}
|
|
mWaitingReadyCallback = !RemoteTextureMap::Get()->CheckRemoteTextureReady(
|
|
wrapper->GetRemoteTextureInfo(), readyCallback);
|
|
if (mWaitingReadyCallback) {
|
|
break;
|
|
}
|
|
RemoteTextureMap::Get()->GetRemoteTexture(wrapper);
|
|
texture = mPendingRemoteTextureWrappers.front();
|
|
mPendingRemoteTextureWrappers.pop_front();
|
|
}
|
|
} else {
|
|
texture = mPendingRemoteTextureWrappers.front();
|
|
auto* wrapper = texture->AsRemoteTextureHostWrapper();
|
|
mPendingRemoteTextureWrappers.pop_front();
|
|
MOZ_ASSERT(mPendingRemoteTextureWrappers.empty());
|
|
|
|
if (mWaitForRemoteTextureOwner) {
|
|
if (StaticPrefs::gfx_remote_texture_wait_owner_at_image_host()) {
|
|
RemoteTextureMap::Get()->WaitForRemoteTextureOwner(wrapper);
|
|
} else {
|
|
wrapper->EnableWaitForRemoteTextureOwner(true);
|
|
}
|
|
}
|
|
mWaitForRemoteTextureOwner = false;
|
|
}
|
|
|
|
if (!texture ||
|
|
(GetAsyncRef() &&
|
|
!texture->AsRemoteTextureHostWrapper()->IsReadyForRendering())) {
|
|
return;
|
|
}
|
|
|
|
SetCurrentTextureHost(texture);
|
|
|
|
if (GetAsyncRef()) {
|
|
for (const auto& it : mWrBridges) {
|
|
RefPtr<WebRenderBridgeParent> wrBridge = it.second->WrBridge();
|
|
if (wrBridge && wrBridge->CompositorScheduler()) {
|
|
wrBridge->CompositorScheduler()->ScheduleComposition(
|
|
wr::RenderReasons::ASYNC_IMAGE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WebRenderImageHost::CleanupResources() {
|
|
ImageComposite::ClearImages();
|
|
SetCurrentTextureHost(nullptr);
|
|
}
|
|
|
|
void WebRenderImageHost::RemoveTextureHost(TextureHost* aTexture) {
|
|
RemoveImagesWithTextureHost(aTexture);
|
|
}
|
|
|
|
void WebRenderImageHost::ClearImages(ClearImagesType aType) {
|
|
ImageComposite::ClearImages();
|
|
if (aType == ClearImagesType::All) {
|
|
if (!mPendingRemoteTextureWrappers.empty()) {
|
|
mPendingRemoteTextureWrappers.clear();
|
|
}
|
|
SetCurrentTextureHost(nullptr);
|
|
|
|
if (GetAsyncRef()) {
|
|
for (const auto& it : mWrBridges) {
|
|
RefPtr<WebRenderBridgeParent> wrBridge = it.second->WrBridge();
|
|
if (wrBridge && wrBridge->CompositorScheduler()) {
|
|
wrBridge->CompositorScheduler()->ScheduleComposition(
|
|
wr::RenderReasons::ASYNC_IMAGE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TimeStamp WebRenderImageHost::GetCompositionTime() const {
|
|
TimeStamp time;
|
|
|
|
MOZ_ASSERT(mCurrentAsyncImageManager);
|
|
if (mCurrentAsyncImageManager) {
|
|
time = mCurrentAsyncImageManager->GetCompositionTime();
|
|
}
|
|
return time;
|
|
}
|
|
|
|
CompositionOpportunityId WebRenderImageHost::GetCompositionOpportunityId()
|
|
const {
|
|
CompositionOpportunityId id;
|
|
|
|
MOZ_ASSERT(mCurrentAsyncImageManager);
|
|
if (mCurrentAsyncImageManager) {
|
|
id = mCurrentAsyncImageManager->GetCompositionOpportunityId();
|
|
}
|
|
return id;
|
|
}
|
|
|
|
void WebRenderImageHost::AppendImageCompositeNotification(
|
|
const ImageCompositeNotificationInfo& aInfo) const {
|
|
if (mCurrentAsyncImageManager) {
|
|
mCurrentAsyncImageManager->AppendImageCompositeNotification(aInfo);
|
|
}
|
|
}
|
|
|
|
TextureHost* WebRenderImageHost::GetAsTextureHostForComposite(
|
|
AsyncImagePipelineManager* aAsyncImageManager) {
|
|
MOZ_ASSERT(aAsyncImageManager);
|
|
|
|
if (mCurrentTextureHost &&
|
|
mCurrentTextureHost->AsRemoteTextureHostWrapper()) {
|
|
return mCurrentTextureHost;
|
|
}
|
|
|
|
mCurrentAsyncImageManager = aAsyncImageManager;
|
|
const auto onExit =
|
|
mozilla::MakeScopeExit([&]() { mCurrentAsyncImageManager = nullptr; });
|
|
|
|
int imageIndex = ChooseImageIndex();
|
|
if (imageIndex < 0) {
|
|
SetCurrentTextureHost(nullptr);
|
|
return nullptr;
|
|
}
|
|
|
|
if (uint32_t(imageIndex) + 1 < ImagesCount()) {
|
|
mCurrentAsyncImageManager->CompositeUntil(
|
|
GetImage(imageIndex + 1)->mTimeStamp +
|
|
TimeDuration::FromMilliseconds(BIAS_TIME_MS));
|
|
}
|
|
|
|
const TimedImage* img = GetImage(imageIndex);
|
|
|
|
RefPtr<TextureHost> texture = img->mTextureHost.get();
|
|
#if XP_WIN
|
|
// Convert YUV BufferTextureHost to TextureHostWrapperD3D11 if possible
|
|
if (texture->AsBufferTextureHost()) {
|
|
auto identifier = aAsyncImageManager->GetTextureFactoryIdentifier();
|
|
const bool tryConvertToNV12 =
|
|
StaticPrefs::gfx_video_convert_yuv_to_nv12_image_host_win() &&
|
|
identifier.mSupportsD3D11NV12 &&
|
|
KnowsCompositor::SupportsD3D11(identifier) &&
|
|
texture->GetFormat() == gfx::SurfaceFormat::YUV420;
|
|
if (tryConvertToNV12) {
|
|
PROFILER_MARKER_TEXT("WebRenderImageHost", GRAPHICS, {},
|
|
"Try ConvertToNV12"_ns);
|
|
|
|
if (!mTextureAllocator) {
|
|
mTextureAllocator = new TextureWrapperD3D11Allocator();
|
|
}
|
|
RefPtr<TextureHost> textureWrapper =
|
|
TextureHostWrapperD3D11::CreateFromBufferTexture(mTextureAllocator,
|
|
texture);
|
|
if (textureWrapper) {
|
|
texture = textureWrapper;
|
|
}
|
|
} else if (profiler_thread_is_being_profiled_for_markers() &&
|
|
StaticPrefs::gfx_video_convert_yuv_to_nv12_image_host_win() &&
|
|
texture->GetFormat() == gfx::SurfaceFormat::YUV420) {
|
|
nsPrintfCString str("No ConvertToNV12 D3D11 %d NV12 %d",
|
|
KnowsCompositor::SupportsD3D11(identifier),
|
|
identifier.mSupportsD3D11NV12);
|
|
PROFILER_MARKER_TEXT("WebRenderImageHost", GRAPHICS, {}, str);
|
|
}
|
|
}
|
|
#endif
|
|
SetCurrentTextureHost(texture);
|
|
|
|
if (mCurrentAsyncImageManager->GetCompositionTime()) {
|
|
// We are in a composition. Send ImageCompositeNotifications.
|
|
OnFinishRendering(imageIndex, img, mAsyncRef.mProcessId, mAsyncRef.mHandle);
|
|
}
|
|
|
|
return mCurrentTextureHost;
|
|
}
|
|
|
|
void WebRenderImageHost::SetCurrentTextureHost(TextureHost* aTexture) {
|
|
if (aTexture == mCurrentTextureHost.get()) {
|
|
return;
|
|
}
|
|
mCurrentTextureHost = aTexture;
|
|
}
|
|
|
|
void WebRenderImageHost::Dump(std::stringstream& aStream, const char* aPrefix,
|
|
bool aDumpHtml) {
|
|
for (const auto& img : Images()) {
|
|
aStream << aPrefix;
|
|
aStream << (aDumpHtml ? "<ul><li>TextureHost: " : "TextureHost: ");
|
|
DumpTextureHost(aStream, img.mTextureHost);
|
|
aStream << (aDumpHtml ? " </li></ul> " : " ");
|
|
}
|
|
}
|
|
|
|
void WebRenderImageHost::SetWrBridge(const wr::PipelineId& aPipelineId,
|
|
WebRenderBridgeParent* aWrBridge) {
|
|
MOZ_ASSERT(aWrBridge);
|
|
MOZ_ASSERT(!mCurrentAsyncImageManager);
|
|
#ifdef DEBUG
|
|
const auto it = mWrBridges.find(wr::AsUint64(aPipelineId));
|
|
MOZ_ASSERT(it == mWrBridges.end());
|
|
#endif
|
|
RefPtr<WebRenderBridgeParentRef> ref =
|
|
aWrBridge->GetWebRenderBridgeParentRef();
|
|
mWrBridges.emplace(wr::AsUint64(aPipelineId), ref);
|
|
}
|
|
|
|
void WebRenderImageHost::ClearWrBridge(const wr::PipelineId& aPipelineId,
|
|
WebRenderBridgeParent* aWrBridge) {
|
|
MOZ_ASSERT(aWrBridge);
|
|
MOZ_ASSERT(!mCurrentAsyncImageManager);
|
|
|
|
const auto it = mWrBridges.find(wr::AsUint64(aPipelineId));
|
|
MOZ_ASSERT(it != mWrBridges.end());
|
|
if (it == mWrBridges.end()) {
|
|
gfxCriticalNote << "WrBridge mismatch happened";
|
|
return;
|
|
}
|
|
mWrBridges.erase(it);
|
|
SetCurrentTextureHost(nullptr);
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|