Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#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() {
if (!mPendingRemoteTextureWrappers.empty()) {
mPendingRemoteTextureWrappers.clear();
}
}
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() {
ClearImages();
SetCurrentTextureHost(nullptr);
}
void WebRenderImageHost::RemoveTextureHost(TextureHost* aTexture) {
CompositableHost::RemoveTextureHost(aTexture);
RemoveImagesWithTextureHost(aTexture);
}
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