1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "RenderCompositorD3D11SWGL.h"
11 #include "mozilla/widget/CompositorWidget.h"
12 #include "mozilla/layers/Effects.h"
13 #include "mozilla/layers/LayerManagerComposite.h"
14 #include "mozilla/webrender/RenderD3D11TextureHost.h"
15 #include "RenderCompositorRecordedFrame.h"
18 using namespace layers
;
22 RenderCompositorD3D11SWGL::UploadMode
23 RenderCompositorD3D11SWGL::GetUploadMode() {
24 int mode
= StaticPrefs::gfx_webrender_software_d3d11_upload_mode();
27 return Upload_Immediate
;
29 return Upload_Staging
;
31 return Upload_StagingNoBlock
;
33 return Upload_StagingPooled
;
35 return Upload_Staging
;
39 UniquePtr
<RenderCompositor
> RenderCompositorD3D11SWGL::Create(
40 RefPtr
<widget::CompositorWidget
>&& aWidget
, nsACString
& aError
) {
41 if (!aWidget
->GetCompositorOptions().AllowSoftwareWebRenderD3D11() ||
42 !gfx::gfxConfig::IsEnabled(gfx::Feature::D3D11_COMPOSITING
)) {
46 void* ctx
= wr_swgl_create_context();
48 gfxCriticalNote
<< "Failed SWGL context creation for WebRender";
52 RefPtr
<CompositorD3D11
> compositor
=
53 MakeAndAddRef
<CompositorD3D11
>(nullptr, aWidget
);
55 if (!compositor
->Initialize(&log
)) {
56 gfxCriticalNote
<< "Failed to initialize CompositorD3D11 for SWGL: "
60 compositor
->UseForSoftwareWebRender();
62 return MakeUnique
<RenderCompositorD3D11SWGL
>(compositor
, std::move(aWidget
),
66 RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL(
67 CompositorD3D11
* aCompositor
, RefPtr
<widget::CompositorWidget
>&& aWidget
,
69 : RenderCompositorLayersSWGL(aCompositor
, std::move(aWidget
), aContext
) {
70 mSyncObject
= GetCompositorD3D11()->GetSyncObject();
73 RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL() {}
75 bool RenderCompositorD3D11SWGL::BeginFrame() {
76 if (!RenderCompositorLayersSWGL::BeginFrame()) {
80 mUploadMode
= GetUploadMode();
84 void RenderCompositorD3D11SWGL::HandleExternalImage(
85 RenderTextureHost
* aExternalImage
, FrameSurface
& aFrameSurface
) {
86 // We need to hold the texture source separately from the effect,
87 // since the effect doesn't hold a strong reference.
88 RefPtr
<DataTextureSourceD3D11
> layer
;
89 RefPtr
<TexturedEffect
> texturedEffect
;
91 if (auto* host
= aExternalImage
->AsRenderDXGITextureHost()) {
92 if (!host
->EnsureD3D11Texture2D(GetDevice())) {
96 layer
= new DataTextureSourceD3D11(GetDevice(), host
->GetFormat(),
97 host
->GetD3D11Texture2D());
98 if (host
->GetFormat() == SurfaceFormat::NV12
||
99 host
->GetFormat() == SurfaceFormat::P010
||
100 host
->GetFormat() == SurfaceFormat::P016
) {
102 new EffectNV12(layer
, host
->GetYUVColorSpace(), host
->GetColorRange(),
103 host
->GetColorDepth(), aFrameSurface
.mFilter
);
105 MOZ_ASSERT(host
->GetFormat() == SurfaceFormat::B8G8R8X8
||
106 host
->GetFormat() == SurfaceFormat::B8G8R8A8
);
107 texturedEffect
= CreateTexturedEffect(host
->GetFormat(), layer
,
108 aFrameSurface
.mFilter
, true);
110 size
= host
->GetSize(0);
111 host
->LockInternal();
112 } else if (auto* host
= aExternalImage
->AsRenderDXGIYCbCrTextureHost()) {
113 if (!host
->EnsureD3D11Texture2D(GetDevice())) {
117 layer
= new DataTextureSourceD3D11(GetDevice(), SurfaceFormat::A8
,
118 host
->GetD3D11Texture2D(0));
119 RefPtr
<DataTextureSourceD3D11
> u
= new DataTextureSourceD3D11(
120 GetDevice(), SurfaceFormat::A8
, host
->GetD3D11Texture2D(1));
121 layer
->SetNextSibling(u
);
122 RefPtr
<DataTextureSourceD3D11
> v
= new DataTextureSourceD3D11(
123 GetDevice(), SurfaceFormat::A8
, host
->GetD3D11Texture2D(2));
124 u
->SetNextSibling(v
);
127 new EffectYCbCr(layer
, host
->GetYUVColorSpace(), host
->GetColorRange(),
128 host
->GetColorDepth(), aFrameSurface
.mFilter
);
129 size
= host
->GetSize(0);
130 host
->LockInternal();
133 gfx::Rect
drawRect(0, 0, size
.width
, size
.height
);
136 effect
.mPrimaryEffect
= texturedEffect
;
137 mCompositor
->DrawQuad(drawRect
, aFrameSurface
.mClipRect
, effect
, 1.0,
138 aFrameSurface
.mTransform
, drawRect
);
140 if (auto* host
= aExternalImage
->AsRenderDXGITextureHost()) {
142 } else if (auto* host
= aExternalImage
->AsRenderDXGIYCbCrTextureHost()) {
147 void RenderCompositorD3D11SWGL::Pause() {}
149 bool RenderCompositorD3D11SWGL::Resume() { return true; }
151 GLenum
RenderCompositorD3D11SWGL::IsContextLost(bool aForce
) {
152 // CompositorD3D11 uses ID3D11Device for composite. The device status needs to
154 auto reason
= GetDevice()->GetDeviceRemovedReason();
157 return LOCAL_GL_NO_ERROR
;
158 case DXGI_ERROR_DEVICE_REMOVED
:
159 case DXGI_ERROR_DRIVER_INTERNAL_ERROR
:
160 NS_WARNING("Device reset due to system / different device");
161 return LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB
;
162 case DXGI_ERROR_DEVICE_HUNG
:
163 case DXGI_ERROR_DEVICE_RESET
:
164 case DXGI_ERROR_INVALID_CALL
:
165 gfxCriticalError() << "Device reset due to WR device: "
166 << gfx::hexa(reason
);
167 return LOCAL_GL_GUILTY_CONTEXT_RESET_ARB
;
169 gfxCriticalError() << "Device reset with WR device unexpected reason: "
170 << gfx::hexa(reason
);
171 return LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB
;
175 UniquePtr
<RenderCompositorLayersSWGL::Surface
>
176 RenderCompositorD3D11SWGL::DoCreateSurface(wr::DeviceIntSize aTileSize
,
178 return MakeUnique
<SurfaceD3D11SWGL
>(aTileSize
, aIsOpaque
);
181 SurfaceD3D11SWGL::SurfaceD3D11SWGL(wr::DeviceIntSize aTileSize
, bool aIsOpaque
)
182 : Surface(aTileSize
, aIsOpaque
) {}
184 RenderCompositorD3D11SWGL::TileD3D11::TileD3D11(
185 layers::DataTextureSourceD3D11
* aTexture
, ID3D11Texture2D
* aStagingTexture
,
186 DataSourceSurface
* aDataSourceSurface
, Surface
* aOwner
,
187 RenderCompositorD3D11SWGL
* aRenderCompositor
)
190 mStagingTexture(aStagingTexture
),
191 mSurface(aDataSourceSurface
),
192 mOwner(aOwner
->AsSurfaceD3D11SWGL()),
193 mRenderCompositor(aRenderCompositor
) {}
195 bool RenderCompositorD3D11SWGL::TileD3D11::Map(wr::DeviceIntRect aDirtyRect
,
196 wr::DeviceIntRect aValidRect
,
197 void** aData
, int32_t* aStride
) {
198 const UploadMode uploadMode
= mRenderCompositor
->GetUploadMode();
199 const gfx::IntSize tileSize
= mOwner
->TileSize();
205 // Check if this tile's upload method matches what we're using for this frame,
206 // and if not then reallocate to fix it. Do this before we copy the struct
207 // into mCurrentTile.
208 if (uploadMode
== Upload_Immediate
) {
209 if (mStagingTexture
) {
210 MOZ_ASSERT(!mSurface
);
211 mStagingTexture
= nullptr;
212 mSurface
= mRenderCompositor
->CreateStagingSurface(tileSize
);
216 MOZ_ASSERT(!mStagingTexture
);
218 mStagingTexture
= mRenderCompositor
->CreateStagingTexture(tileSize
);
222 mRenderCompositor
->mCurrentStagingTexture
= mStagingTexture
;
223 mRenderCompositor
->mCurrentStagingTextureIsTemp
= false;
225 if (uploadMode
== Upload_Immediate
) {
226 DataSourceSurface::MappedSurface map
;
227 if (!mSurface
->Map(DataSourceSurface::READ_WRITE
, &map
)) {
232 map
.mData
+ aValidRect
.origin
.y
* map
.mStride
+ aValidRect
.origin
.x
* 4;
233 *aStride
= map
.mStride
;
234 mValidRect
= gfx::Rect(aValidRect
.origin
.x
, aValidRect
.origin
.y
,
235 aValidRect
.size
.width
, aValidRect
.size
.height
);
239 if (!mRenderCompositor
->mCurrentStagingTexture
) {
243 RefPtr
<ID3D11DeviceContext
> context
;
244 mRenderCompositor
->GetDevice()->GetImmediateContext(getter_AddRefs(context
));
246 D3D11_MAPPED_SUBRESOURCE mappedSubresource
;
248 bool shouldBlock
= uploadMode
== Upload_Staging
;
250 HRESULT hr
= context
->Map(
251 mRenderCompositor
->mCurrentStagingTexture
, 0, D3D11_MAP_READ_WRITE
,
252 shouldBlock
? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT
, &mappedSubresource
);
253 if (hr
== DXGI_ERROR_WAS_STILL_DRAWING
) {
254 // mCurrentTile is a copy of the real tile data, so we can just replace the
255 // staging one with a temporary for this draw. The staging texture for the
256 // real tile remains untouched, so we'll go back to using that for future
257 // frames and discard the new one. In the future we could improve this by
258 // having a pool of shared staging textures for all the tiles.
260 // Mark the tile as having a temporary staging texture.
261 mRenderCompositor
->mCurrentStagingTextureIsTemp
= true;
263 // Try grabbing a texture from the staging pool and see if we can use that.
264 if (uploadMode
== Upload_StagingPooled
&& mOwner
->mStagingPool
.Length()) {
265 mRenderCompositor
->mCurrentStagingTexture
=
266 mOwner
->mStagingPool
.ElementAt(0);
267 mOwner
->mStagingPool
.RemoveElementAt(0);
268 hr
= context
->Map(mRenderCompositor
->mCurrentStagingTexture
, 0,
269 D3D11_MAP_READ_WRITE
, D3D11_MAP_FLAG_DO_NOT_WAIT
,
272 // If that failed, put it back into the pool (but at the end).
273 if (hr
== DXGI_ERROR_WAS_STILL_DRAWING
) {
274 mOwner
->mStagingPool
.AppendElement(
275 mRenderCompositor
->mCurrentStagingTexture
);
279 // No staging textures, or we tried one and it was busy. Allocate a brand
281 if (hr
== DXGI_ERROR_WAS_STILL_DRAWING
) {
282 mRenderCompositor
->mCurrentStagingTexture
=
283 mRenderCompositor
->CreateStagingTexture(tileSize
);
284 if (!mRenderCompositor
->mCurrentStagingTexture
) {
287 hr
= context
->Map(mRenderCompositor
->mCurrentStagingTexture
, 0,
288 D3D11_MAP_READ_WRITE
, 0, &mappedSubresource
);
291 MOZ_ASSERT(SUCCEEDED(hr
));
293 // aData is expected to contain a pointer to the first pixel within the valid
294 // rect, so take the mapped resource's data (which covers the full tile size)
295 // and offset it by the top/left of the valid rect.
296 *aData
= (uint8_t*)mappedSubresource
.pData
+
297 aValidRect
.origin
.y
* mappedSubresource
.RowPitch
+
298 aValidRect
.origin
.x
* 4;
299 *aStride
= mappedSubresource
.RowPitch
;
301 // Store the new valid rect, so that we can composite only those pixels
302 mValidRect
= gfx::Rect(aValidRect
.origin
.x
, aValidRect
.origin
.y
,
303 aValidRect
.size
.width
, aValidRect
.size
.height
);
308 void RenderCompositorD3D11SWGL::TileD3D11::Unmap(
309 const gfx::IntRect
& aDirtyRect
) {
310 const UploadMode uploadMode
= mRenderCompositor
->GetUploadMode();
317 MOZ_ASSERT(uploadMode
== Upload_Immediate
);
319 nsIntRegion
dirty(aDirtyRect
);
320 // This uses UpdateSubresource, which blocks, so is likely implemented as a
321 // memcpy into driver owned memory, followed by an async upload. The staging
322 // method should avoid this extra copy, and is likely to be faster usually.
323 // We could possible do this call on a background thread so that sw-wr can
324 // start drawing the next tile while the memcpy is in progress.
325 mTexture
->Update(mSurface
, &dirty
);
329 if (!mRenderCompositor
->mCurrentStagingTexture
) {
333 RefPtr
<ID3D11DeviceContext
> context
;
334 mRenderCompositor
->GetDevice()->GetImmediateContext(getter_AddRefs(context
));
336 context
->Unmap(mRenderCompositor
->mCurrentStagingTexture
, 0);
341 box
.left
= aDirtyRect
.X();
342 box
.top
= aDirtyRect
.Y();
343 box
.right
= aDirtyRect
.XMost();
344 box
.bottom
= aDirtyRect
.YMost();
346 context
->CopySubresourceRegion(
347 mTexture
->GetD3D11Texture(), 0, aDirtyRect
.x
, aDirtyRect
.y
, 0,
348 mRenderCompositor
->mCurrentStagingTexture
, 0, &box
);
350 // If we allocated a temp staging texture for this tile, and we're running
351 // in pooled mode, then consider adding it to the pool for later.
352 if (mRenderCompositor
->mCurrentStagingTextureIsTemp
&&
353 uploadMode
== Upload_StagingPooled
) {
354 static const uint32_t kMaxPoolSize
= 5;
355 if (mOwner
->mStagingPool
.Length() < kMaxPoolSize
) {
356 mOwner
->mStagingPool
.AppendElement(
357 mRenderCompositor
->mCurrentStagingTexture
);
361 mRenderCompositor
->mCurrentStagingTexture
= nullptr;
362 mRenderCompositor
->mCurrentStagingTextureIsTemp
= false;
365 bool RenderCompositorD3D11SWGL::TileD3D11::IsValid() { return !!mTexture
; }
367 already_AddRefed
<ID3D11Texture2D
>
368 RenderCompositorD3D11SWGL::CreateStagingTexture(const gfx::IntSize aSize
) {
369 CD3D11_TEXTURE2D_DESC
desc(DXGI_FORMAT_B8G8R8A8_UNORM
, aSize
.width
,
372 desc
.CPUAccessFlags
= D3D11_CPU_ACCESS_WRITE
| D3D11_CPU_ACCESS_READ
;
373 desc
.Usage
= D3D11_USAGE_STAGING
;
376 RefPtr
<ID3D11Texture2D
> cpuTexture
;
377 DebugOnly
<HRESULT
> hr
=
378 GetDevice()->CreateTexture2D(&desc
, nullptr, getter_AddRefs(cpuTexture
));
379 MOZ_ASSERT(SUCCEEDED(hr
));
381 gfxCriticalNote
<< "Failed to create StagingTexture: " << aSize
;
382 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
384 return cpuTexture
.forget();
387 already_AddRefed
<DataSourceSurface
>
388 RenderCompositorD3D11SWGL::CreateStagingSurface(const gfx::IntSize aSize
) {
389 return Factory::CreateDataSourceSurface(aSize
, SurfaceFormat::B8G8R8A8
);
392 UniquePtr
<RenderCompositorLayersSWGL::Tile
>
393 RenderCompositorD3D11SWGL::DoCreateTile(Surface
* aSurface
) {
394 MOZ_RELEASE_ASSERT(aSurface
);
396 const auto tileSize
= aSurface
->TileSize();
398 if (mUploadMode
== Upload_Immediate
) {
399 RefPtr
<DataTextureSourceD3D11
> source
=
400 new DataTextureSourceD3D11(gfx::SurfaceFormat::B8G8R8A8
, mCompositor
,
401 layers::TextureFlags::NO_FLAGS
);
402 RefPtr
<DataSourceSurface
> surf
= CreateStagingSurface(tileSize
);
403 return MakeUnique
<TileD3D11
>(source
, nullptr, surf
, aSurface
, this);
406 MOZ_ASSERT(mUploadMode
== Upload_Staging
||
407 mUploadMode
== Upload_StagingNoBlock
||
408 mUploadMode
== Upload_StagingPooled
);
410 CD3D11_TEXTURE2D_DESC
desc(DXGI_FORMAT_B8G8R8A8_UNORM
, tileSize
.width
,
411 tileSize
.height
, 1, 1);
413 RefPtr
<ID3D11Texture2D
> texture
;
414 DebugOnly
<HRESULT
> hr
=
415 GetDevice()->CreateTexture2D(&desc
, nullptr, getter_AddRefs(texture
));
416 MOZ_ASSERT(SUCCEEDED(hr
));
418 gfxCriticalNote
<< "Failed to allocate Texture2D: " << aSurface
->TileSize();
419 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
420 return MakeUnique
<TileD3D11
>(nullptr, nullptr, nullptr, aSurface
, this);
423 RefPtr
<DataTextureSourceD3D11
> source
= new DataTextureSourceD3D11(
424 GetDevice(), gfx::SurfaceFormat::B8G8R8A8
, texture
);
426 RefPtr
<ID3D11Texture2D
> cpuTexture
= CreateStagingTexture(tileSize
);
427 return MakeUnique
<TileD3D11
>(source
, cpuTexture
, nullptr, aSurface
, this);
430 bool RenderCompositorD3D11SWGL::MaybeReadback(
431 const gfx::IntSize
& aReadbackSize
, const wr::ImageFormat
& aReadbackFormat
,
432 const Range
<uint8_t>& aReadbackBuffer
, bool* aNeedsYFlip
) {
433 MOZ_ASSERT(aReadbackFormat
== wr::ImageFormat::BGRA8
);
436 aReadbackSize
.width
* gfx::BytesPerPixel(SurfaceFormat::B8G8R8A8
);
437 RefPtr
<gfx::DrawTarget
> dt
= gfx::Factory::CreateDrawTargetForData(
438 gfx::BackendType::SKIA
, &aReadbackBuffer
[0], aReadbackSize
, stride
,
439 SurfaceFormat::B8G8R8A8
, false);
444 GetCompositorD3D11()->Readback(dt
);
449 } // namespace mozilla