commit b5f886f686b8fae03056e01409c67b6ef64cb4e2
parent a144cd932b075502dbb655a085e2439f3741d717
Author: Sotaro Ikeda <sotaro.ikeda.g@gmail.com>
Date: Wed, 29 Oct 2025 22:32:54 +0000
Bug 1958604 - Add a capability to use IDCompositionTexture with layer compositor r=gfx-reviewers,lsalzman
The change does rendering with full render.
Partial update is going to be supported by another bug.
Differential Revision: https://phabricator.services.mozilla.com/D270363
Diffstat:
6 files changed, 313 insertions(+), 3 deletions(-)
diff --git a/gfx/config/gfxVars.h b/gfx/config/gfxVars.h
@@ -117,7 +117,8 @@ class MOZ_STACK_CLASS gfxVarsCollectUpdates;
_(GPUProcessEnabled, bool, false) \
_(DMABufModifiersP010, ArrayOfuint64_t, nsTArray<uint64_t>()) \
_(DMABufModifiersNV12, ArrayOfuint64_t, nsTArray<uint64_t>()) \
- _(AllowGLNorm16Textures, bool, false)
+ _(AllowGLNorm16Textures, bool, false) \
+ _(WebRenderLayerCompositorDCompTexture, bool, false)
/* Add new entries above this line. */
diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp
@@ -2861,6 +2861,14 @@ void gfxPlatform::InitWebRenderConfig() {
gfxVars::SetUseWebRenderTripleBufferingWin(true);
}
}
+
+# ifdef XP_WIN
+ if (StaticPrefs::
+ gfx_webrender_layer_compositor_use_dcomp_texture_AtStartup() &&
+ IsWin1122H2OrLater() && gfxVars::UseWebRenderDCompWin()) {
+ gfxVars::SetWebRenderLayerCompositorDCompTexture(true);
+ }
+# endif
#endif
bool allowOverlayVpAutoHDR = false;
diff --git a/gfx/webrender_bindings/DCLayerTree.cpp b/gfx/webrender_bindings/DCLayerTree.cpp
@@ -750,6 +750,9 @@ void DCLayerTree::PresentSwapChain(wr::NativeSurfaceId aId,
size_t aNumDirtyRects) {
auto surface = GetSurface(aId);
surface->AsDCLayerSurface()->Present(aDirtyRects, aNumDirtyRects);
+ if (surface->AsDCLayerDCompositionTexture()) {
+ mPendingCommit = true;
+ }
}
void DCLayerTree::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset,
@@ -845,7 +848,15 @@ void DCLayerTree::CreateSwapChainSurface(wr::NativeSurfaceId aId,
MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
UniquePtr<DCSurface> surface;
- if (!mEnableAsyncScreenshot &&
+ if (SupportsDCompositionTexture()) {
+ surface = MakeUnique<DCLayerDCompositionTexture>(aSize, aIsOpaque, this);
+ if (!surface->Initialize()) {
+ gfxCriticalNote << "Failed to initialize DCLayerDCompositionTexture: "
+ << wr::AsUint64(aId);
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ }
+ } else if (
+ !mEnableAsyncScreenshot &&
(aNeedsSyncDcompCommit ||
StaticPrefs::
gfx_webrender_layer_compositor_force_composition_surface_AtStartup())) {
@@ -1309,6 +1320,36 @@ bool DCLayerTree::SupportsSwapChainTearing() {
return supported;
}
+bool DCLayerTree::SupportsDCompositionTexture() {
+ RefPtr<ID3D11Device> device = mDevice;
+ RefPtr<IDCompositionDevice2> compositionDevice = mCompositionDevice;
+ static const bool supported = [device, compositionDevice] {
+ const auto dcomp4 = QI<IDCompositionDevice4>::From(compositionDevice.get());
+ if (!dcomp4) {
+ return false;
+ }
+
+ BOOL supportCompositionTexture = FALSE;
+ HRESULT hr = dcomp4->CheckCompositionTextureSupport(
+ device, &supportCompositionTexture);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ if (supportCompositionTexture == FALSE) {
+ return false;
+ }
+
+ return true;
+ }();
+
+ if (!gfx::gfxVars::WebRenderLayerCompositorDCompTexture()) {
+ return false;
+ }
+
+ return supported;
+}
+
DXGI_FORMAT DCLayerTree::GetOverlayFormatForSDR() {
return sGpuOverlayInfo->mOverlayFormatUsed;
}
@@ -1525,6 +1566,203 @@ DCTile* DCSurface::GetTile(int32_t aX, int32_t aY) const {
return tile_it->second.get();
}
+DCLayerDCompositionTexture::TextureHolder::TextureHolder(
+ ID3D11Texture2D* aTexture, IDCompositionTexture* aDCompositionTexture,
+ EGLSurface aEGLSurface)
+ : mTexture(aTexture),
+ mDCompositionTexture(aDCompositionTexture),
+ mEGLSurface(aEGLSurface) {}
+
+DCLayerDCompositionTexture::DCLayerDCompositionTexture(
+ wr::DeviceIntSize aSize, bool aIsOpaque, DCLayerTree* aDCLayerTree)
+ : DCLayerSurface(aIsOpaque, aDCLayerTree),
+ mSwapChainBufferCount(gfx::gfxVars::UseWebRenderTripleBufferingWin() ? 3
+ : 2),
+ mSize(aSize) {}
+
+DCLayerDCompositionTexture::~DCLayerDCompositionTexture() { DestroyTextures(); }
+
+bool DCLayerDCompositionTexture::Initialize() {
+ DCSurface::Initialize();
+
+ if (!AllocateTextures()) {
+ return false;
+ }
+ return true;
+}
+
+bool DCLayerDCompositionTexture::AllocateTextures() {
+ MOZ_ASSERT(mAvailableTextureHolders.empty());
+
+ HRESULT hr;
+ const auto device = mDCLayerTree->GetDevice();
+ const auto dcomp = mDCLayerTree->GetCompositionDevice();
+ const auto dcomp4 = QI<IDCompositionDevice4>::From(dcomp);
+ if (!dcomp4) {
+ return false;
+ }
+
+ const auto gl = mDCLayerTree->GetGLContext();
+ const auto& gle = gl::GLContextEGL::Cast(gl);
+ const auto& egl = gle->mEgl;
+ const EGLConfig eglConfig = mDCLayerTree->GetEGLConfig();
+
+ CD3D11_TEXTURE2D_DESC desc(
+ DXGI_FORMAT_B8G8R8A8_UNORM, mSize.width, mSize.height, 1, 1,
+ D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET);
+
+ desc.MiscFlags =
+ D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED;
+
+ for (size_t i = 0; i < mSwapChainBufferCount; i++) {
+ // Allocate ID3D11Texture2D
+ RefPtr<ID3D11Texture2D> texture;
+ hr = device->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+ if (FAILED(hr)) {
+ gfxCriticalNoteOnce << "CreateTexture2D failed: " << gfx::hexa(hr);
+ return false;
+ }
+
+ // Allocate IDCompositionTexture
+ RefPtr<IDCompositionTexture> dcompTexture;
+ hr =
+ dcomp4->CreateCompositionTexture(texture, getter_AddRefs(dcompTexture));
+ if (FAILED(hr)) {
+ gfxCriticalNoteOnce << "CreateCompositionTexture failed: "
+ << gfx::hexa(hr);
+ return false;
+ }
+
+ const auto alphaMode =
+ mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
+ dcompTexture->SetAlphaMode(alphaMode);
+ // XXX
+ // dcompTexture->SetColorSpace();
+
+ // Allocate mEGLSurface
+ EGLSurface surface = EGL_NO_SURFACE;
+ const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, mSize.width,
+ LOCAL_EGL_HEIGHT, mSize.height,
+ LOCAL_EGL_NONE};
+ const auto buffer = reinterpret_cast<EGLClientBuffer>(texture.get());
+
+ surface = egl->fCreatePbufferFromClientBuffer(
+ LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, eglConfig, pbuffer_attribs);
+ if (!surface) {
+ EGLint err = egl->mLib->fGetError();
+ gfxCriticalNote << "Failed to create Pbuffer error: " << gfx::hexa(err)
+ << " Size : "
+ << LayoutDeviceIntSize(mSize.width, mSize.height);
+ return false;
+ }
+
+ auto textureHolder =
+ MakeUnique<TextureHolder>(texture, dcompTexture, surface);
+ mAvailableTextureHolders.push_back(std::move(textureHolder));
+ }
+
+ MOZ_ASSERT(mAvailableTextureHolders.size() == mSwapChainBufferCount);
+
+ return true;
+}
+
+void DCLayerDCompositionTexture::DestroyTextures() {
+ const auto gl = mDCLayerTree->GetGLContext();
+ const auto& gle = gl::GLContextEGL::Cast(gl);
+ const auto& egl = gle->mEgl;
+
+ if (mCurrentTextureHolder) {
+ mAvailableTextureHolders.push_back(std::move(mCurrentTextureHolder));
+ }
+
+ if (mPresentingTextureHolder) {
+ mAvailableTextureHolders.push_back(std::move(mPresentingTextureHolder));
+ }
+
+ while (!mAvailableTextureHolders.empty()) {
+ auto& front = mAvailableTextureHolders.front();
+
+ if (front->mEGLSurface) {
+ if (gle->GetEGLSurfaceOverride() == front->mEGLSurface) {
+ gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
+ }
+ egl->fDestroySurface(front->mEGLSurface);
+ front->mEGLSurface = EGL_NO_SURFACE;
+ }
+
+ mAvailableTextureHolders.pop_front();
+ }
+
+ MOZ_ASSERT(!mCurrentTextureHolder);
+ MOZ_ASSERT(!mPresentingTextureHolder);
+ MOZ_ASSERT(mAvailableTextureHolders.empty());
+}
+
+UniquePtr<DCLayerDCompositionTexture::TextureHolder>
+DCLayerDCompositionTexture::GetNextTexture() {
+ MOZ_ASSERT(!mAvailableTextureHolders.empty());
+
+ if (mAvailableTextureHolders.empty()) {
+ return nullptr;
+ }
+
+ UniquePtr<TextureHolder> textureHolder =
+ std::move(mAvailableTextureHolders.front());
+ mAvailableTextureHolders.pop_front();
+
+ return textureHolder;
+}
+
+void DCLayerDCompositionTexture::UpdateCurrentTexture() {
+ if (mCurrentTextureHolder) {
+ mAvailableTextureHolders.push_back(std::move(mCurrentTextureHolder));
+ }
+
+ MOZ_ASSERT(!mCurrentTextureHolder);
+
+ mCurrentTextureHolder = GetNextTexture();
+}
+
+void DCLayerDCompositionTexture::Bind(const wr::DeviceIntRect* aDirtyRects,
+ size_t aNumDirtyRects) {
+ UpdateCurrentTexture();
+
+ if (!mCurrentTextureHolder ||
+ (mCurrentTextureHolder->mEGLSurface == EGL_NO_SURFACE)) {
+ return;
+ }
+
+ const auto gl = mDCLayerTree->GetGLContext();
+ const auto& gle = gl::GLContextEGL::Cast(gl);
+
+ gle->SetEGLSurfaceOverride(mCurrentTextureHolder->mEGLSurface);
+}
+
+bool DCLayerDCompositionTexture::Resize(wr::DeviceIntSize aSize) {
+ DestroyTextures();
+ mSize = aSize;
+ bool ret = AllocateTextures();
+ return ret;
+}
+
+void DCLayerDCompositionTexture::Present(const wr::DeviceIntRect* aDirtyRects,
+ size_t aNumDirtyRects) {
+ if (!mCurrentTextureHolder) {
+ return;
+ }
+
+ if (mPresentingTextureHolder) {
+ mAvailableTextureHolders.push_back(std::move(mPresentingTextureHolder));
+ }
+ MOZ_ASSERT(!mPresentingTextureHolder);
+
+ mPresentingTextureHolder = std::move(mCurrentTextureHolder);
+ MOZ_ASSERT(!mCurrentTextureHolder);
+ MOZ_ASSERT(mPresentingTextureHolder);
+
+ mContentVisual->SetContent(mPresentingTextureHolder->mDCompositionTexture);
+}
+
DCSwapChain::DCSwapChain(wr::DeviceIntSize aSize, bool aIsOpaque,
DCLayerTree* aDCLayerTree)
: DCLayerSurface(aIsOpaque, aDCLayerTree),
diff --git a/gfx/webrender_bindings/DCLayerTree.h b/gfx/webrender_bindings/DCLayerTree.h
@@ -7,6 +7,7 @@
#ifndef MOZILLA_GFX_DCLAYER_TREE_H
#define MOZILLA_GFX_DCLAYER_TREE_H
+#include <deque>
#include <dxgiformat.h>
#include <unordered_map>
#include <vector>
@@ -24,6 +25,7 @@
struct ID3D11Device;
struct ID3D11DeviceContext;
+struct ID3D11Texture2D;
struct ID3D11VideoDevice;
struct ID3D11VideoContext;
struct ID3D11VideoProcessor;
@@ -32,6 +34,7 @@ struct ID3D11VideoProcessorOutputView;
struct IDCompositionColorMatrixEffect;
struct IDCompositionFilterEffect;
struct IDCompositionTableTransferEffect;
+struct IDCompositionTexture;
struct IDCompositionDevice2;
struct IDCompositionDevice3;
struct IDCompositionSurface;
@@ -61,6 +64,7 @@ namespace wr {
class DCLayerSurface;
class DCTile;
+class DCLayerDCompositionTexture;
class DCSurface;
class DCSwapChain;
class DCSurfaceVideo;
@@ -195,6 +199,7 @@ class DCLayerTree {
DXGI_FORMAT GetOverlayFormatForSDR();
bool SupportsSwapChainTearing();
+ bool SupportsDCompositionTexture();
void SetUsedOverlayTypeInFrame(DCompOverlayTypes aTypes);
@@ -352,6 +357,9 @@ class DCSurface {
virtual DCSurfaceHandle* AsDCSurfaceHandle() { return nullptr; }
virtual DCLayerSurface* AsDCLayerSurface() { return nullptr; }
virtual DCSwapChain* AsDCSwapChain() { return nullptr; }
+ virtual DCLayerDCompositionTexture* AsDCLayerDCompositionTexture() {
+ return nullptr;
+ }
bool IsUpdated(const wr::CompositorSurfaceTransform& aTransform,
const wr::DeviceIntRect& aClipRect,
@@ -429,6 +437,50 @@ class DCLayerSurface : public DCSurface {
DCLayerSurface* AsDCLayerSurface() override { return this; }
};
+class DCLayerDCompositionTexture : public DCLayerSurface {
+ public:
+ DCLayerDCompositionTexture(wr::DeviceIntSize aSize, bool aIsOpaque,
+ DCLayerTree* aDCLayerTree);
+ virtual ~DCLayerDCompositionTexture();
+
+ bool Initialize() override;
+
+ void Bind(const wr::DeviceIntRect* aDirtyRects,
+ size_t aNumDirtyRects) override;
+ bool Resize(wr::DeviceIntSize aSize) override;
+ void Present(const wr::DeviceIntRect* aDirtyRects,
+ size_t aNumDirtyRects) override;
+
+ DCLayerDCompositionTexture* AsDCLayerDCompositionTexture() override {
+ return this;
+ }
+
+ const size_t mSwapChainBufferCount;
+
+ private:
+ struct TextureHolder {
+ TextureHolder(ID3D11Texture2D* aTexture,
+ IDCompositionTexture* aDCompositionTexture,
+ EGLSurface aEGLSurface);
+ TextureHolder() = default;
+
+ RefPtr<ID3D11Texture2D> mTexture;
+ RefPtr<IDCompositionTexture> mDCompositionTexture;
+ EGLSurface mEGLSurface;
+ };
+
+ bool AllocateTextures();
+ void DestroyTextures();
+ UniquePtr<TextureHolder> GetNextTexture();
+ void UpdateCurrentTexture();
+
+ wr::DeviceIntSize mSize;
+ std::deque<UniquePtr<TextureHolder>> mAvailableTextureHolders;
+
+ UniquePtr<TextureHolder> mCurrentTextureHolder;
+ UniquePtr<TextureHolder> mPresentingTextureHolder;
+};
+
class DCSwapChain : public DCLayerSurface {
public:
DCSwapChain(wr::DeviceIntSize aSize, bool aIsOpaque,
diff --git a/gfx/webrender_bindings/RenderCompositorANGLE.cpp b/gfx/webrender_bindings/RenderCompositorANGLE.cpp
@@ -1007,7 +1007,13 @@ void RenderCompositorANGLE::InitializeUsePartialPresent() {
bool RenderCompositorANGLE::UsePartialPresent() { return mUsePartialPresent; }
-bool RenderCompositorANGLE::RequestFullRender() { return mFullRender; }
+bool RenderCompositorANGLE::RequestFullRender() {
+ // XXX Remove when partial update is supported.
+ if (UseLayerCompositor() && mDCLayerTree->SupportsDCompositionTexture()) {
+ return true;
+ }
+ return mFullRender;
+}
uint32_t RenderCompositorANGLE::GetMaxPartialPresentRects() {
if (!mUsePartialPresent) {
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
@@ -7690,6 +7690,11 @@
type: bool
value: false
mirror: once
+
+- name: gfx.webrender.layer-compositor-use-dcomp-texture
+ type: bool
+ value: false
+ mirror: once
#endif
- name: gfx.webrender.scissored-cache-clears.enabled