summaryrefslogtreecommitdiffstats
path: root/gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp')
-rw-r--r--gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp484
1 files changed, 484 insertions, 0 deletions
diff --git a/gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp b/gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp
new file mode 100644
index 0000000000..1fdae182c2
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp
@@ -0,0 +1,484 @@
+/* -*- 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 "RenderCompositorD3D11SWGL.h"
+
+#include "gfxConfig.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/widget/CompositorWidget.h"
+#include "mozilla/layers/Effects.h"
+#include "mozilla/webrender/RenderD3D11TextureHost.h"
+#include "RenderCompositorRecordedFrame.h"
+#include "RenderThread.h"
+
+namespace mozilla {
+using namespace layers;
+
+namespace wr {
+
+extern LazyLogModule gRenderThreadLog;
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+RenderCompositorD3D11SWGL::UploadMode
+RenderCompositorD3D11SWGL::GetUploadMode() {
+ int mode = StaticPrefs::gfx_webrender_software_d3d11_upload_mode();
+ switch (mode) {
+ case 1:
+ return Upload_Immediate;
+ case 2:
+ return Upload_Staging;
+ case 3:
+ return Upload_StagingNoBlock;
+ case 4:
+ return Upload_StagingPooled;
+ default:
+ return Upload_Staging;
+ }
+}
+
+UniquePtr<RenderCompositor> RenderCompositorD3D11SWGL::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+ if (!aWidget->GetCompositorOptions().AllowSoftwareWebRenderD3D11() ||
+ !gfx::gfxConfig::IsEnabled(gfx::Feature::D3D11_COMPOSITING)) {
+ return nullptr;
+ }
+
+ void* ctx = wr_swgl_create_context();
+ if (!ctx) {
+ gfxCriticalNote << "Failed SWGL context creation for WebRender";
+ return nullptr;
+ }
+
+ RefPtr<CompositorD3D11> compositor = MakeAndAddRef<CompositorD3D11>(aWidget);
+ nsCString log;
+ if (!compositor->Initialize(&log)) {
+ gfxCriticalNote << "Failed to initialize CompositorD3D11 for SWGL: "
+ << log.get();
+ return nullptr;
+ }
+ return MakeUnique<RenderCompositorD3D11SWGL>(compositor, aWidget, ctx);
+}
+
+RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL(
+ CompositorD3D11* aCompositor,
+ const RefPtr<widget::CompositorWidget>& aWidget, void* aContext)
+ : RenderCompositorLayersSWGL(aCompositor, aWidget, aContext) {
+ LOG("RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL()");
+
+ mSyncObject = GetCompositorD3D11()->GetSyncObject();
+}
+
+RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL() {
+ LOG("RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL()");
+}
+
+bool RenderCompositorD3D11SWGL::BeginFrame() {
+ if (!RenderCompositorLayersSWGL::BeginFrame()) {
+ return false;
+ }
+
+ mUploadMode = GetUploadMode();
+ return true;
+}
+
+void RenderCompositorD3D11SWGL::HandleExternalImage(
+ RenderTextureHost* aExternalImage, FrameSurface& aFrameSurface) {
+ // We need to hold the texture source separately from the effect,
+ // since the effect doesn't hold a strong reference.
+ RefPtr<DataTextureSourceD3D11> layer;
+ RefPtr<TexturedEffect> texturedEffect;
+ gfx::IntSize size;
+ if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
+ if (!host->EnsureD3D11Texture2D(GetDevice())) {
+ return;
+ }
+
+ layer = new DataTextureSourceD3D11(GetDevice(), host->GetFormat(),
+ host->GetD3D11Texture2D());
+ if (host->GetFormat() == gfx::SurfaceFormat::NV12 ||
+ host->GetFormat() == gfx::SurfaceFormat::P010 ||
+ host->GetFormat() == gfx::SurfaceFormat::P016) {
+ const auto yuv = FromYUVRangedColorSpace(host->GetYUVColorSpace());
+ texturedEffect =
+ new EffectNV12(layer, yuv.space, yuv.range, host->GetColorDepth(),
+ aFrameSurface.mFilter);
+ } else {
+ MOZ_ASSERT(host->GetFormat() == gfx::SurfaceFormat::B8G8R8X8 ||
+ host->GetFormat() == gfx::SurfaceFormat::B8G8R8A8);
+ texturedEffect = CreateTexturedEffect(host->GetFormat(), layer,
+ aFrameSurface.mFilter, true);
+ }
+ size = host->GetSize(0);
+ host->LockInternal();
+ } else if (auto* host = aExternalImage->AsRenderDXGIYCbCrTextureHost()) {
+ if (!host->EnsureD3D11Texture2D(GetDevice())) {
+ return;
+ }
+
+ layer = new DataTextureSourceD3D11(GetDevice(), gfx::SurfaceFormat::A8,
+ host->GetD3D11Texture2D(0));
+ RefPtr<DataTextureSourceD3D11> u = new DataTextureSourceD3D11(
+ GetDevice(), gfx::SurfaceFormat::A8, host->GetD3D11Texture2D(1));
+ layer->SetNextSibling(u);
+ RefPtr<DataTextureSourceD3D11> v = new DataTextureSourceD3D11(
+ GetDevice(), gfx::SurfaceFormat::A8, host->GetD3D11Texture2D(2));
+ u->SetNextSibling(v);
+
+ const auto yuv = FromYUVRangedColorSpace(host->GetYUVColorSpace());
+ texturedEffect =
+ new EffectYCbCr(layer, yuv.space, yuv.range, host->GetColorDepth(),
+ aFrameSurface.mFilter);
+ size = host->GetSize(0);
+ host->LockInternal();
+ }
+
+ gfx::Rect drawRect(0, 0, size.width, size.height);
+
+ EffectChain effect;
+ effect.mPrimaryEffect = texturedEffect;
+ mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0,
+ aFrameSurface.mTransform, drawRect);
+
+ if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
+ host->Unlock();
+ } else if (auto* host = aExternalImage->AsRenderDXGIYCbCrTextureHost()) {
+ host->Unlock();
+ }
+}
+
+void RenderCompositorD3D11SWGL::Pause() {}
+
+bool RenderCompositorD3D11SWGL::Resume() { return true; }
+
+GLenum RenderCompositorD3D11SWGL::IsContextLost(bool aForce) {
+ // CompositorD3D11 uses ID3D11Device for composite. The device status needs to
+ // be checked.
+ auto reason = GetDevice()->GetDeviceRemovedReason();
+ switch (reason) {
+ case S_OK:
+ return LOCAL_GL_NO_ERROR;
+ case DXGI_ERROR_DEVICE_REMOVED:
+ case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
+ NS_WARNING("Device reset due to system / different device");
+ return LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB;
+ case DXGI_ERROR_DEVICE_HUNG:
+ case DXGI_ERROR_DEVICE_RESET:
+ case DXGI_ERROR_INVALID_CALL:
+ gfxCriticalError() << "Device reset due to WR device: "
+ << gfx::hexa(reason);
+ return LOCAL_GL_GUILTY_CONTEXT_RESET_ARB;
+ default:
+ gfxCriticalError() << "Device reset with WR device unexpected reason: "
+ << gfx::hexa(reason);
+ return LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
+ }
+}
+
+UniquePtr<RenderCompositorLayersSWGL::Surface>
+RenderCompositorD3D11SWGL::DoCreateSurface(wr::DeviceIntSize aTileSize,
+ bool aIsOpaque) {
+ return MakeUnique<SurfaceD3D11SWGL>(aTileSize, aIsOpaque);
+}
+
+SurfaceD3D11SWGL::SurfaceD3D11SWGL(wr::DeviceIntSize aTileSize, bool aIsOpaque)
+ : Surface(aTileSize, aIsOpaque) {}
+
+RenderCompositorD3D11SWGL::TileD3D11::TileD3D11(
+ layers::DataTextureSourceD3D11* aTexture, ID3D11Texture2D* aStagingTexture,
+ gfx::DataSourceSurface* aDataSourceSurface, Surface* aOwner,
+ RenderCompositorD3D11SWGL* aRenderCompositor)
+ : Tile(),
+ mTexture(aTexture),
+ mStagingTexture(aStagingTexture),
+ mSurface(aDataSourceSurface),
+ mOwner(aOwner->AsSurfaceD3D11SWGL()),
+ mRenderCompositor(aRenderCompositor) {}
+
+bool RenderCompositorD3D11SWGL::TileD3D11::Map(wr::DeviceIntRect aDirtyRect,
+ wr::DeviceIntRect aValidRect,
+ void** aData, int32_t* aStride) {
+ const UploadMode uploadMode = mRenderCompositor->GetUploadMode();
+ const gfx::IntSize tileSize = mOwner->TileSize();
+
+ if (!IsValid()) {
+ return false;
+ }
+
+ // Check if this tile's upload method matches what we're using for this frame,
+ // and if not then reallocate to fix it. Do this before we copy the struct
+ // into mCurrentTile.
+ if (uploadMode == Upload_Immediate) {
+ if (mStagingTexture) {
+ MOZ_ASSERT(!mSurface);
+ mStagingTexture = nullptr;
+ mSurface = mRenderCompositor->CreateStagingSurface(tileSize);
+ }
+ } else {
+ if (mSurface) {
+ MOZ_ASSERT(!mStagingTexture);
+ mSurface = nullptr;
+ mStagingTexture = mRenderCompositor->CreateStagingTexture(tileSize);
+ }
+ }
+
+ mRenderCompositor->mCurrentStagingTexture = mStagingTexture;
+ mRenderCompositor->mCurrentStagingTextureIsTemp = false;
+
+ if (uploadMode == Upload_Immediate) {
+ gfx::DataSourceSurface::MappedSurface map;
+ if (!mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) {
+ return false;
+ }
+
+ *aData = map.mData + aValidRect.min.y * map.mStride + aValidRect.min.x * 4;
+ *aStride = map.mStride;
+ // Ensure our mapped data is accessible by writing to the beginning and end
+ // of the dirty region. See bug 171519
+ uint32_t* probeData = (uint32_t*)map.mData +
+ aDirtyRect.min.y * (map.mStride / 4) +
+ aDirtyRect.min.x;
+ *probeData = 0;
+ uint32_t* probeDataEnd = (uint32_t*)map.mData +
+ (aDirtyRect.max.y - 1) * (map.mStride / 4) +
+ (aDirtyRect.max.x - 1);
+ *probeDataEnd = 0;
+
+ mValidRect = gfx::Rect(aValidRect.min.x, aValidRect.min.y,
+ aValidRect.width(), aValidRect.height());
+ return true;
+ }
+
+ if (!mRenderCompositor->mCurrentStagingTexture) {
+ return false;
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ mRenderCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context));
+
+ D3D11_MAPPED_SUBRESOURCE mappedSubresource;
+
+ bool shouldBlock = uploadMode == Upload_Staging;
+
+ HRESULT hr = context->Map(
+ mRenderCompositor->mCurrentStagingTexture, 0, D3D11_MAP_READ_WRITE,
+ shouldBlock ? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT, &mappedSubresource);
+ if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
+ // mCurrentTile is a copy of the real tile data, so we can just replace the
+ // staging one with a temporary for this draw. The staging texture for the
+ // real tile remains untouched, so we'll go back to using that for future
+ // frames and discard the new one. In the future we could improve this by
+ // having a pool of shared staging textures for all the tiles.
+
+ // Mark the tile as having a temporary staging texture.
+ mRenderCompositor->mCurrentStagingTextureIsTemp = true;
+
+ // Try grabbing a texture from the staging pool and see if we can use that.
+ if (uploadMode == Upload_StagingPooled && mOwner->mStagingPool.Length()) {
+ mRenderCompositor->mCurrentStagingTexture =
+ mOwner->mStagingPool.ElementAt(0);
+ mOwner->mStagingPool.RemoveElementAt(0);
+ hr = context->Map(mRenderCompositor->mCurrentStagingTexture, 0,
+ D3D11_MAP_READ_WRITE, D3D11_MAP_FLAG_DO_NOT_WAIT,
+ &mappedSubresource);
+
+ // If that failed, put it back into the pool (but at the end).
+ if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
+ mOwner->mStagingPool.AppendElement(
+ mRenderCompositor->mCurrentStagingTexture);
+ }
+ }
+
+ // No staging textures, or we tried one and it was busy. Allocate a brand
+ // new one instead.
+ if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
+ mRenderCompositor->mCurrentStagingTexture =
+ mRenderCompositor->CreateStagingTexture(tileSize);
+ if (!mRenderCompositor->mCurrentStagingTexture) {
+ return false;
+ }
+ hr = context->Map(mRenderCompositor->mCurrentStagingTexture, 0,
+ D3D11_MAP_READ_WRITE, 0, &mappedSubresource);
+ }
+ }
+ if (!SUCCEEDED(hr)) {
+ gfxCriticalError() << "Failed to map tile: " << gfx::hexa(hr);
+ // This is only expected to fail if we hit a device reset.
+ MOZ_RELEASE_ASSERT(
+ mRenderCompositor->GetDevice()->GetDeviceRemovedReason() != S_OK);
+ return false;
+ }
+
+ // aData is expected to contain a pointer to the first pixel within the valid
+ // rect, so take the mapped resource's data (which covers the full tile size)
+ // and offset it by the top/left of the valid rect.
+ *aData = (uint8_t*)mappedSubresource.pData +
+ aValidRect.min.y * mappedSubresource.RowPitch + aValidRect.min.x * 4;
+ *aStride = mappedSubresource.RowPitch;
+
+ // Ensure our mapped data is accessible by writing to the beginning and end
+ // of the dirty region. See bug 171519
+ uint32_t* probeData = (uint32_t*)mappedSubresource.pData +
+ aDirtyRect.min.y * (mappedSubresource.RowPitch / 4) +
+ aDirtyRect.min.x;
+ *probeData = 0;
+ uint32_t* probeDataEnd =
+ (uint32_t*)mappedSubresource.pData +
+ (aDirtyRect.max.y - 1) * (mappedSubresource.RowPitch / 4) +
+ (aDirtyRect.max.x - 1);
+ *probeDataEnd = 0;
+
+ // Store the new valid rect, so that we can composite only those pixels
+ mValidRect = gfx::Rect(aValidRect.min.x, aValidRect.min.y, aValidRect.width(),
+ aValidRect.height());
+
+ return true;
+}
+
+void RenderCompositorD3D11SWGL::TileD3D11::Unmap(
+ const gfx::IntRect& aDirtyRect) {
+ const UploadMode uploadMode = mRenderCompositor->GetUploadMode();
+
+ if (!IsValid()) {
+ return;
+ }
+
+ if (mSurface) {
+ MOZ_ASSERT(uploadMode == Upload_Immediate);
+ mSurface->Unmap();
+ nsIntRegion dirty(aDirtyRect);
+ // This uses UpdateSubresource, which blocks, so is likely implemented as a
+ // memcpy into driver owned memory, followed by an async upload. The staging
+ // method should avoid this extra copy, and is likely to be faster usually.
+ // We could possible do this call on a background thread so that sw-wr can
+ // start drawing the next tile while the memcpy is in progress.
+ mTexture->Update(mSurface, &dirty);
+ return;
+ }
+
+ if (!mRenderCompositor->mCurrentStagingTexture) {
+ return;
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ mRenderCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context));
+
+ context->Unmap(mRenderCompositor->mCurrentStagingTexture, 0);
+
+ D3D11_BOX box;
+ box.front = 0;
+ box.back = 1;
+ box.left = aDirtyRect.X();
+ box.top = aDirtyRect.Y();
+ box.right = aDirtyRect.XMost();
+ box.bottom = aDirtyRect.YMost();
+
+ context->CopySubresourceRegion(
+ mTexture->GetD3D11Texture(), 0, aDirtyRect.x, aDirtyRect.y, 0,
+ mRenderCompositor->mCurrentStagingTexture, 0, &box);
+
+ // If we allocated a temp staging texture for this tile, and we're running
+ // in pooled mode, then consider adding it to the pool for later.
+ if (mRenderCompositor->mCurrentStagingTextureIsTemp &&
+ uploadMode == Upload_StagingPooled) {
+ static const uint32_t kMaxPoolSize = 5;
+ if (mOwner->mStagingPool.Length() < kMaxPoolSize) {
+ mOwner->mStagingPool.AppendElement(
+ mRenderCompositor->mCurrentStagingTexture);
+ }
+ }
+
+ mRenderCompositor->mCurrentStagingTexture = nullptr;
+ mRenderCompositor->mCurrentStagingTextureIsTemp = false;
+}
+
+bool RenderCompositorD3D11SWGL::TileD3D11::IsValid() { return !!mTexture; }
+
+already_AddRefed<ID3D11Texture2D>
+RenderCompositorD3D11SWGL::CreateStagingTexture(const gfx::IntSize aSize) {
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, aSize.width,
+ aSize.height, 1, 1);
+
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
+ desc.Usage = D3D11_USAGE_STAGING;
+ desc.BindFlags = 0;
+
+ RefPtr<ID3D11Texture2D> cpuTexture;
+ DebugOnly<HRESULT> hr =
+ GetDevice()->CreateTexture2D(&desc, nullptr, getter_AddRefs(cpuTexture));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (!cpuTexture) {
+ gfxCriticalNote << "Failed to create StagingTexture: " << aSize;
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ }
+ return cpuTexture.forget();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+RenderCompositorD3D11SWGL::CreateStagingSurface(const gfx::IntSize aSize) {
+ return gfx::Factory::CreateDataSourceSurface(aSize,
+ gfx::SurfaceFormat::B8G8R8A8);
+}
+
+UniquePtr<RenderCompositorLayersSWGL::Tile>
+RenderCompositorD3D11SWGL::DoCreateTile(Surface* aSurface) {
+ MOZ_RELEASE_ASSERT(aSurface);
+
+ const auto tileSize = aSurface->TileSize();
+
+ if (mUploadMode == Upload_Immediate) {
+ RefPtr<DataTextureSourceD3D11> source =
+ new DataTextureSourceD3D11(gfx::SurfaceFormat::B8G8R8A8, mCompositor,
+ layers::TextureFlags::NO_FLAGS);
+ RefPtr<gfx::DataSourceSurface> surf = CreateStagingSurface(tileSize);
+ return MakeUnique<TileD3D11>(source, nullptr, surf, aSurface, this);
+ }
+
+ MOZ_ASSERT(mUploadMode == Upload_Staging ||
+ mUploadMode == Upload_StagingNoBlock ||
+ mUploadMode == Upload_StagingPooled);
+
+ CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, tileSize.width,
+ tileSize.height, 1, 1);
+
+ RefPtr<ID3D11Texture2D> texture;
+ DebugOnly<HRESULT> hr =
+ GetDevice()->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (!texture) {
+ gfxCriticalNote << "Failed to allocate Texture2D: " << aSurface->TileSize();
+ RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
+ return MakeUnique<TileD3D11>(nullptr, nullptr, nullptr, aSurface, this);
+ }
+
+ RefPtr<DataTextureSourceD3D11> source = new DataTextureSourceD3D11(
+ GetDevice(), gfx::SurfaceFormat::B8G8R8A8, texture);
+
+ RefPtr<ID3D11Texture2D> cpuTexture = CreateStagingTexture(tileSize);
+ return MakeUnique<TileD3D11>(source, cpuTexture, nullptr, aSurface, this);
+}
+
+bool RenderCompositorD3D11SWGL::MaybeReadback(
+ const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
+ const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
+ MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
+
+ auto stride =
+ aReadbackSize.width * gfx::BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8);
+ RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
+ gfx::BackendType::SKIA, &aReadbackBuffer[0], aReadbackSize, stride,
+ gfx::SurfaceFormat::B8G8R8A8, false);
+ if (!dt) {
+ return false;
+ }
+
+ GetCompositorD3D11()->Readback(dt);
+ return true;
+}
+
+} // namespace wr
+} // namespace mozilla