diff --git a/gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp b/gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp index 573c035bf19f..9193bc1b8f4f 100644 --- a/gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp +++ b/gfx/webrender_bindings/RenderCompositorD3D11SWGL.cpp @@ -19,6 +19,23 @@ using namespace layers; namespace wr { +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 RenderCompositorD3D11SWGL::Create( RefPtr&& aWidget, nsACString& aError) { void* ctx = wr_swgl_create_context(); @@ -69,6 +86,7 @@ bool RenderCompositorD3D11SWGL::BeginFrame() { return false; } mInFrame = true; + mUploadMode = GetUploadMode(); return true; } @@ -248,17 +266,86 @@ bool RenderCompositorD3D11SWGL::MapTile(wr::NativeTileId aId, auto layerCursor = surface.mTiles.find(TileKey(aId.x, aId.y)); MOZ_RELEASE_ASSERT(layerCursor != surface.mTiles.end()); + + // 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 (mUploadMode == Upload_Immediate) { + if (layerCursor->second.mStagingTexture) { + MOZ_ASSERT(!layerCursor->second.mSurface); + layerCursor->second.mStagingTexture = nullptr; + layerCursor->second.mSurface = CreateStagingSurface(surface.TileSize()); + } + } else { + if (layerCursor->second.mSurface) { + MOZ_ASSERT(!layerCursor->second.mStagingTexture); + layerCursor->second.mSurface = nullptr; + layerCursor->second.mStagingTexture = + CreateStagingTexture(surface.TileSize()); + } + } + mCurrentTile = layerCursor->second; + mCurrentTileId = aId; mCurrentTileDirty = IntRect(aDirtyRect.origin.x, aDirtyRect.origin.y, aDirtyRect.size.width, aDirtyRect.size.height); + if (mUploadMode == Upload_Immediate) { + DataSourceSurface::MappedSurface map; + if (!mCurrentTile.mSurface->Map(DataSourceSurface::READ_WRITE, &map)) { + return false; + } + + *aData = + map.mData + aValidRect.origin.y * map.mStride + aValidRect.origin.x * 4; + *aStride = map.mStride; + layerCursor->second.mValidRect = + gfx::Rect(aValidRect.origin.x, aValidRect.origin.y, + aValidRect.size.width, aValidRect.size.height); + return true; + } + RefPtr context; mCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context)); D3D11_MAPPED_SUBRESOURCE mappedSubresource; - DebugOnly hr = - context->Map(mCurrentTile.mStagingTexture, 0, D3D11_MAP_READ_WRITE, 0, - &mappedSubresource); + + bool shouldBlock = mUploadMode == Upload_Staging; + + HRESULT hr = context->Map( + mCurrentTile.mStagingTexture, 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. + mCurrentTile.mIsTemp = true; + + // Try grabbing a texture from the staging pool and see if we can use that. + if (mUploadMode == Upload_StagingPooled && surface.mStagingPool.Length()) { + mCurrentTile.mStagingTexture = surface.mStagingPool.ElementAt(0); + surface.mStagingPool.RemoveElementAt(0); + hr = context->Map(mCurrentTile.mStagingTexture, 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) { + surface.mStagingPool.AppendElement(mCurrentTile.mStagingTexture); + } + } + + // No staging textures, or we tried one and it was busy. Allocate a brand + // new one instead. + if (hr == DXGI_ERROR_WAS_STILL_DRAWING) { + mCurrentTile.mStagingTexture = CreateStagingTexture(surface.TileSize()); + hr = context->Map(mCurrentTile.mStagingTexture, 0, D3D11_MAP_READ_WRITE, + 0, &mappedSubresource); + } + } MOZ_ASSERT(SUCCEEDED(hr)); // aData is expected to contain a pointer to the first pixel within the valid @@ -277,6 +364,19 @@ bool RenderCompositorD3D11SWGL::MapTile(wr::NativeTileId aId, } void RenderCompositorD3D11SWGL::UnmapTile() { + if (mCurrentTile.mSurface) { + MOZ_ASSERT(mUploadMode == Upload_Immediate); + mCurrentTile.mSurface->Unmap(); + nsIntRegion dirty(mCurrentTileDirty); + // 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. + mCurrentTile.mTexture->Update(mCurrentTile.mSurface, &dirty); + return; + } + RefPtr context; mCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context)); @@ -293,6 +393,20 @@ void RenderCompositorD3D11SWGL::UnmapTile() { context->CopySubresourceRegion(mCurrentTile.mTexture->GetD3D11Texture(), 0, mCurrentTileDirty.x, mCurrentTileDirty.y, 0, mCurrentTile.mStagingTexture, 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 (mCurrentTile.mIsTemp && mUploadMode == Upload_StagingPooled) { + auto surfaceCursor = mSurfaces.find(mCurrentTileId.surface_id); + MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end()); + Surface& surface = surfaceCursor->second; + + static const uint32_t kMaxPoolSize = 5; + if (surface.mStagingPool.Length() < kMaxPoolSize) { + surface.mStagingPool.AppendElement(mCurrentTile.mStagingTexture); + } + } + mCurrentTile.mStagingTexture = nullptr; } void RenderCompositorD3D11SWGL::CreateSurface(wr::NativeSurfaceId aId, @@ -317,6 +431,27 @@ void RenderCompositorD3D11SWGL::DestroySurface(NativeSurfaceId aId) { mSurfaces.erase(surfaceCursor); } +already_AddRefed +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 cpuTexture; + DebugOnly hr = mCompositor->GetDevice()->CreateTexture2D( + &desc, nullptr, getter_AddRefs(cpuTexture)); + MOZ_ASSERT(SUCCEEDED(hr)); + return cpuTexture.forget(); +} + +already_AddRefed +RenderCompositorD3D11SWGL::CreateStagingSurface(const gfx::IntSize aSize) { + return Factory::CreateDataSourceSurface(aSize, SurfaceFormat::B8G8R8A8); +} + void RenderCompositorD3D11SWGL::CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) { auto surfaceCursor = mSurfaces.find(aId); @@ -324,6 +459,19 @@ void RenderCompositorD3D11SWGL::CreateTile(wr::NativeSurfaceId aId, int32_t aX, Surface& surface = surfaceCursor->second; MOZ_RELEASE_ASSERT(!surface.mIsExternal); + if (mUploadMode == Upload_Immediate) { + RefPtr source = + new DataTextureSourceD3D11(gfx::SurfaceFormat::B8G8R8A8, mCompositor, + layers::TextureFlags::NO_FLAGS); + RefPtr surf = CreateStagingSurface(surface.TileSize()); + surface.mTiles.insert({TileKey(aX, aY), Tile{source, nullptr, surf}}); + return; + } + + MOZ_ASSERT(mUploadMode == Upload_Staging || + mUploadMode == Upload_StagingNoBlock || + mUploadMode == Upload_StagingPooled); + CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, surface.mTileSize.width, surface.mTileSize.height, 1, 1); @@ -336,16 +484,8 @@ void RenderCompositorD3D11SWGL::CreateTile(wr::NativeSurfaceId aId, int32_t aX, RefPtr source = new DataTextureSourceD3D11( mCompositor->GetDevice(), gfx::SurfaceFormat::B8G8R8A8, texture); - desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ; - desc.Usage = D3D11_USAGE_STAGING; - desc.BindFlags = 0; - - RefPtr cpuTexture; - hr = mCompositor->GetDevice()->CreateTexture2D(&desc, nullptr, - getter_AddRefs(cpuTexture)); - MOZ_ASSERT(SUCCEEDED(hr)); - - surface.mTiles.insert({TileKey(aX, aY), Tile{source, cpuTexture}}); + RefPtr cpuTexture = CreateStagingTexture(surface.TileSize()); + surface.mTiles.insert({TileKey(aX, aY), Tile{source, cpuTexture, nullptr}}); } void RenderCompositorD3D11SWGL::DestroyTile(wr::NativeSurfaceId aId, int32_t aX, diff --git a/gfx/webrender_bindings/RenderCompositorD3D11SWGL.h b/gfx/webrender_bindings/RenderCompositorD3D11SWGL.h index 9a845490a566..77fcc6461d07 100644 --- a/gfx/webrender_bindings/RenderCompositorD3D11SWGL.h +++ b/gfx/webrender_bindings/RenderCompositorD3D11SWGL.h @@ -107,6 +107,11 @@ class RenderCompositorD3D11SWGL : public RenderCompositor { ID3D11Device* GetDevice() { return mCompositor->GetDevice(); } private: + already_AddRefed CreateStagingTexture( + const gfx::IntSize aSize); + already_AddRefed CreateStagingSurface( + const gfx::IntSize aSize); + RefPtr mCompositor; void* mContext = nullptr; @@ -116,7 +121,9 @@ class RenderCompositorD3D11SWGL : public RenderCompositor { // changed area into the texture. RefPtr mTexture; RefPtr mStagingTexture; + RefPtr mSurface; gfx::Rect mValidRect; + bool mIsTemp = false; struct KeyHashFn { std::size_t operator()(const TileKey& aId) const { @@ -141,6 +148,8 @@ class RenderCompositorD3D11SWGL : public RenderCompositor { std::unordered_map mTiles; RefPtr mExternalImage; + nsTArray> mStagingPool; + struct IdHashFn { std::size_t operator()(const wr::NativeSurfaceId& aId) const { return HashGeneric(wr::AsUint64(aId)); @@ -149,9 +158,19 @@ class RenderCompositorD3D11SWGL : public RenderCompositor { }; std::unordered_map mSurfaces; + enum UploadMode { + Upload_Immediate, + Upload_Staging, + Upload_StagingNoBlock, + Upload_StagingPooled + }; + UploadMode GetUploadMode(); + UploadMode mUploadMode = Upload_Staging; + // Temporary state held between MapTile and UnmapTile Tile mCurrentTile; gfx::IntRect mCurrentTileDirty; + wr::NativeTileId mCurrentTileId; // The set of surfaces added to be composited for the current frame struct FrameSurface { diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index db584cc4796d..c01b6cfebf84 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -4793,6 +4793,11 @@ value: true mirror: once +- name: gfx.webrender.software.d3d11.upload-mode + type: RelaxedAtomicInt32 + value: 2 + mirror: always + # Use vsync events generated by hardware - name: gfx.work-around-driver-bugs type: bool