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 "gfxConfig.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/widget/CompositorWidget.h"
14 #include "mozilla/layers/Effects.h"
15 #include "mozilla/webrender/RenderD3D11TextureHost.h"
16 #include "RenderCompositorRecordedFrame.h"
17 #include "RenderThread.h"
20 using namespace layers
;
24 extern LazyLogModule gRenderThreadLog
;
25 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
27 RenderCompositorD3D11SWGL::UploadMode
28 RenderCompositorD3D11SWGL::GetUploadMode() {
29 int mode
= StaticPrefs::gfx_webrender_software_d3d11_upload_mode();
32 return Upload_Immediate
;
34 return Upload_Staging
;
36 return Upload_StagingNoBlock
;
38 return Upload_StagingPooled
;
40 return Upload_Staging
;
44 UniquePtr
<RenderCompositor
> RenderCompositorD3D11SWGL::Create(
45 const RefPtr
<widget::CompositorWidget
>& aWidget
, nsACString
& aError
) {
46 if (!aWidget
->GetCompositorOptions().AllowSoftwareWebRenderD3D11() ||
47 !gfx::gfxConfig::IsEnabled(gfx::Feature::D3D11_COMPOSITING
)) {
51 void* ctx
= wr_swgl_create_context();
53 gfxCriticalNote
<< "Failed SWGL context creation for WebRender";
57 RefPtr
<CompositorD3D11
> compositor
= MakeAndAddRef
<CompositorD3D11
>(aWidget
);
59 if (!compositor
->Initialize(&log
)) {
60 gfxCriticalNote
<< "Failed to initialize CompositorD3D11 for SWGL: "
64 return MakeUnique
<RenderCompositorD3D11SWGL
>(compositor
, aWidget
, ctx
);
67 RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL(
68 CompositorD3D11
* aCompositor
,
69 const RefPtr
<widget::CompositorWidget
>& aWidget
, void* aContext
)
70 : RenderCompositorLayersSWGL(aCompositor
, aWidget
, aContext
) {
71 LOG("RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL()");
73 mSyncObject
= GetCompositorD3D11()->GetSyncObject();
76 RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL() {
77 LOG("RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL()");
80 bool RenderCompositorD3D11SWGL::BeginFrame() {
81 if (!RenderCompositorLayersSWGL::BeginFrame()) {
85 mUploadMode
= GetUploadMode();
89 void RenderCompositorD3D11SWGL::HandleExternalImage(
90 RenderTextureHost
* aExternalImage
, FrameSurface
& aFrameSurface
) {
91 // We need to hold the texture source separately from the effect,
92 // since the effect doesn't hold a strong reference.
93 RefPtr
<DataTextureSourceD3D11
> layer
;
94 RefPtr
<TexturedEffect
> texturedEffect
;
96 if (auto* host
= aExternalImage
->AsRenderDXGITextureHost()) {
97 if (!host
->EnsureD3D11Texture2D(GetDevice())) {
101 layer
= new DataTextureSourceD3D11(GetDevice(), host
->GetFormat(),
102 host
->GetD3D11Texture2D());
103 if (host
->GetFormat() == gfx::SurfaceFormat::NV12
||
104 host
->GetFormat() == gfx::SurfaceFormat::P010
||
105 host
->GetFormat() == gfx::SurfaceFormat::P016
) {
106 const auto yuv
= FromYUVRangedColorSpace(host
->GetYUVColorSpace());
108 new EffectNV12(layer
, yuv
.space
, yuv
.range
, host
->GetColorDepth(),
109 aFrameSurface
.mFilter
);
111 MOZ_ASSERT(host
->GetFormat() == gfx::SurfaceFormat::B8G8R8X8
||
112 host
->GetFormat() == gfx::SurfaceFormat::B8G8R8A8
);
113 texturedEffect
= CreateTexturedEffect(host
->GetFormat(), layer
,
114 aFrameSurface
.mFilter
, true);
116 size
= host
->GetSize(0);
117 host
->LockInternal();
118 } else if (auto* host
= aExternalImage
->AsRenderDXGIYCbCrTextureHost()) {
119 if (!host
->EnsureD3D11Texture2D(GetDevice())) {
123 layer
= new DataTextureSourceD3D11(GetDevice(), gfx::SurfaceFormat::A8
,
124 host
->GetD3D11Texture2D(0));
125 RefPtr
<DataTextureSourceD3D11
> u
= new DataTextureSourceD3D11(
126 GetDevice(), gfx::SurfaceFormat::A8
, host
->GetD3D11Texture2D(1));
127 layer
->SetNextSibling(u
);
128 RefPtr
<DataTextureSourceD3D11
> v
= new DataTextureSourceD3D11(
129 GetDevice(), gfx::SurfaceFormat::A8
, host
->GetD3D11Texture2D(2));
130 u
->SetNextSibling(v
);
132 const auto yuv
= FromYUVRangedColorSpace(host
->GetYUVColorSpace());
134 new EffectYCbCr(layer
, yuv
.space
, yuv
.range
, host
->GetColorDepth(),
135 aFrameSurface
.mFilter
);
136 size
= host
->GetSize(0);
137 host
->LockInternal();
140 gfx::Rect
drawRect(0, 0, size
.width
, size
.height
);
143 effect
.mPrimaryEffect
= texturedEffect
;
144 mCompositor
->DrawQuad(drawRect
, aFrameSurface
.mClipRect
, effect
, 1.0,
145 aFrameSurface
.mTransform
, drawRect
);
147 if (auto* host
= aExternalImage
->AsRenderDXGITextureHost()) {
149 } else if (auto* host
= aExternalImage
->AsRenderDXGIYCbCrTextureHost()) {
154 void RenderCompositorD3D11SWGL::Pause() {}
156 bool RenderCompositorD3D11SWGL::Resume() { return true; }
158 GLenum
RenderCompositorD3D11SWGL::IsContextLost(bool aForce
) {
159 // CompositorD3D11 uses ID3D11Device for composite. The device status needs to
161 auto reason
= GetDevice()->GetDeviceRemovedReason();
164 return LOCAL_GL_NO_ERROR
;
165 case DXGI_ERROR_DEVICE_REMOVED
:
166 case DXGI_ERROR_DRIVER_INTERNAL_ERROR
:
167 NS_WARNING("Device reset due to system / different device");
168 return LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB
;
169 case DXGI_ERROR_DEVICE_HUNG
:
170 case DXGI_ERROR_DEVICE_RESET
:
171 case DXGI_ERROR_INVALID_CALL
:
172 gfxCriticalError() << "Device reset due to WR device: "
173 << gfx::hexa(reason
);
174 return LOCAL_GL_GUILTY_CONTEXT_RESET_ARB
;
176 gfxCriticalError() << "Device reset with WR device unexpected reason: "
177 << gfx::hexa(reason
);
178 return LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB
;
182 UniquePtr
<RenderCompositorLayersSWGL::Surface
>
183 RenderCompositorD3D11SWGL::DoCreateSurface(wr::DeviceIntSize aTileSize
,
185 return MakeUnique
<SurfaceD3D11SWGL
>(aTileSize
, aIsOpaque
);
188 SurfaceD3D11SWGL::SurfaceD3D11SWGL(wr::DeviceIntSize aTileSize
, bool aIsOpaque
)
189 : Surface(aTileSize
, aIsOpaque
) {}
191 RenderCompositorD3D11SWGL::TileD3D11::TileD3D11(
192 layers::DataTextureSourceD3D11
* aTexture
, ID3D11Texture2D
* aStagingTexture
,
193 gfx::DataSourceSurface
* aDataSourceSurface
, Surface
* aOwner
,
194 RenderCompositorD3D11SWGL
* aRenderCompositor
)
197 mStagingTexture(aStagingTexture
),
198 mSurface(aDataSourceSurface
),
199 mOwner(aOwner
->AsSurfaceD3D11SWGL()),
200 mRenderCompositor(aRenderCompositor
) {}
202 bool RenderCompositorD3D11SWGL::TileD3D11::Map(wr::DeviceIntRect aDirtyRect
,
203 wr::DeviceIntRect aValidRect
,
204 void** aData
, int32_t* aStride
) {
205 const UploadMode uploadMode
= mRenderCompositor
->GetUploadMode();
206 const gfx::IntSize tileSize
= mOwner
->TileSize();
212 // Check if this tile's upload method matches what we're using for this frame,
213 // and if not then reallocate to fix it. Do this before we copy the struct
214 // into mCurrentTile.
215 if (uploadMode
== Upload_Immediate
) {
216 if (mStagingTexture
) {
217 MOZ_ASSERT(!mSurface
);
218 mStagingTexture
= nullptr;
219 mSurface
= mRenderCompositor
->CreateStagingSurface(tileSize
);
223 MOZ_ASSERT(!mStagingTexture
);
225 mStagingTexture
= mRenderCompositor
->CreateStagingTexture(tileSize
);
229 mRenderCompositor
->mCurrentStagingTexture
= mStagingTexture
;
230 mRenderCompositor
->mCurrentStagingTextureIsTemp
= false;
232 if (uploadMode
== Upload_Immediate
) {
233 gfx::DataSourceSurface::MappedSurface map
;
234 if (!mSurface
->Map(gfx::DataSourceSurface::READ_WRITE
, &map
)) {
238 *aData
= map
.mData
+ aValidRect
.min
.y
* map
.mStride
+ aValidRect
.min
.x
* 4;
239 *aStride
= map
.mStride
;
240 // Ensure our mapped data is accessible by writing to the beginning and end
241 // of the dirty region. See bug 1717519
242 if (aDirtyRect
.width() > 0 && aDirtyRect
.height() > 0) {
243 uint32_t* probeData
= (uint32_t*)map
.mData
+
244 aDirtyRect
.min
.y
* (map
.mStride
/ 4) +
247 uint32_t* probeDataEnd
= (uint32_t*)map
.mData
+
248 (aDirtyRect
.max
.y
- 1) * (map
.mStride
/ 4) +
249 (aDirtyRect
.max
.x
- 1);
253 mValidRect
= gfx::Rect(aValidRect
.min
.x
, aValidRect
.min
.y
,
254 aValidRect
.width(), aValidRect
.height());
258 if (!mRenderCompositor
->mCurrentStagingTexture
) {
262 RefPtr
<ID3D11DeviceContext
> context
;
263 mRenderCompositor
->GetDevice()->GetImmediateContext(getter_AddRefs(context
));
265 D3D11_MAPPED_SUBRESOURCE mappedSubresource
;
267 bool shouldBlock
= uploadMode
== Upload_Staging
;
269 HRESULT hr
= context
->Map(
270 mRenderCompositor
->mCurrentStagingTexture
, 0, D3D11_MAP_READ_WRITE
,
271 shouldBlock
? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT
, &mappedSubresource
);
272 if (hr
== DXGI_ERROR_WAS_STILL_DRAWING
) {
273 // mCurrentTile is a copy of the real tile data, so we can just replace the
274 // staging one with a temporary for this draw. The staging texture for the
275 // real tile remains untouched, so we'll go back to using that for future
276 // frames and discard the new one. In the future we could improve this by
277 // having a pool of shared staging textures for all the tiles.
279 // Mark the tile as having a temporary staging texture.
280 mRenderCompositor
->mCurrentStagingTextureIsTemp
= true;
282 // Try grabbing a texture from the staging pool and see if we can use that.
283 if (uploadMode
== Upload_StagingPooled
&& mOwner
->mStagingPool
.Length()) {
284 mRenderCompositor
->mCurrentStagingTexture
=
285 mOwner
->mStagingPool
.ElementAt(0);
286 mOwner
->mStagingPool
.RemoveElementAt(0);
287 hr
= context
->Map(mRenderCompositor
->mCurrentStagingTexture
, 0,
288 D3D11_MAP_READ_WRITE
, D3D11_MAP_FLAG_DO_NOT_WAIT
,
291 // If that failed, put it back into the pool (but at the end).
292 if (hr
== DXGI_ERROR_WAS_STILL_DRAWING
) {
293 mOwner
->mStagingPool
.AppendElement(
294 mRenderCompositor
->mCurrentStagingTexture
);
298 // No staging textures, or we tried one and it was busy. Allocate a brand
300 if (hr
== DXGI_ERROR_WAS_STILL_DRAWING
) {
301 mRenderCompositor
->mCurrentStagingTexture
=
302 mRenderCompositor
->CreateStagingTexture(tileSize
);
303 if (!mRenderCompositor
->mCurrentStagingTexture
) {
306 hr
= context
->Map(mRenderCompositor
->mCurrentStagingTexture
, 0,
307 D3D11_MAP_READ_WRITE
, 0, &mappedSubresource
);
310 if (!SUCCEEDED(hr
)) {
311 gfxCriticalError() << "Failed to map tile: " << gfx::hexa(hr
);
312 // This is only expected to fail if we hit a device reset.
314 mRenderCompositor
->GetDevice()->GetDeviceRemovedReason() != S_OK
);
318 // aData is expected to contain a pointer to the first pixel within the valid
319 // rect, so take the mapped resource's data (which covers the full tile size)
320 // and offset it by the top/left of the valid rect.
321 *aData
= (uint8_t*)mappedSubresource
.pData
+
322 aValidRect
.min
.y
* mappedSubresource
.RowPitch
+ aValidRect
.min
.x
* 4;
323 *aStride
= mappedSubresource
.RowPitch
;
325 // Ensure our mapped data is accessible by writing to the beginning and end
326 // of the dirty region. See bug 1717519
327 if (aDirtyRect
.width() > 0 && aDirtyRect
.height() > 0) {
328 uint32_t* probeData
= (uint32_t*)mappedSubresource
.pData
+
329 aDirtyRect
.min
.y
* (mappedSubresource
.RowPitch
/ 4) +
332 uint32_t* probeDataEnd
=
333 (uint32_t*)mappedSubresource
.pData
+
334 (aDirtyRect
.max
.y
- 1) * (mappedSubresource
.RowPitch
/ 4) +
335 (aDirtyRect
.max
.x
- 1);
339 // Store the new valid rect, so that we can composite only those pixels
340 mValidRect
= gfx::Rect(aValidRect
.min
.x
, aValidRect
.min
.y
, aValidRect
.width(),
341 aValidRect
.height());
346 void RenderCompositorD3D11SWGL::TileD3D11::Unmap(
347 const gfx::IntRect
& aDirtyRect
) {
348 const UploadMode uploadMode
= mRenderCompositor
->GetUploadMode();
355 MOZ_ASSERT(uploadMode
== Upload_Immediate
);
357 nsIntRegion
dirty(aDirtyRect
);
358 // This uses UpdateSubresource, which blocks, so is likely implemented as a
359 // memcpy into driver owned memory, followed by an async upload. The staging
360 // method should avoid this extra copy, and is likely to be faster usually.
361 // We could possible do this call on a background thread so that sw-wr can
362 // start drawing the next tile while the memcpy is in progress.
363 mTexture
->Update(mSurface
, &dirty
);
367 if (!mRenderCompositor
->mCurrentStagingTexture
) {
371 RefPtr
<ID3D11DeviceContext
> context
;
372 mRenderCompositor
->GetDevice()->GetImmediateContext(getter_AddRefs(context
));
374 context
->Unmap(mRenderCompositor
->mCurrentStagingTexture
, 0);
379 box
.left
= aDirtyRect
.X();
380 box
.top
= aDirtyRect
.Y();
381 box
.right
= aDirtyRect
.XMost();
382 box
.bottom
= aDirtyRect
.YMost();
384 context
->CopySubresourceRegion(
385 mTexture
->GetD3D11Texture(), 0, aDirtyRect
.x
, aDirtyRect
.y
, 0,
386 mRenderCompositor
->mCurrentStagingTexture
, 0, &box
);
388 // If we allocated a temp staging texture for this tile, and we're running
389 // in pooled mode, then consider adding it to the pool for later.
390 if (mRenderCompositor
->mCurrentStagingTextureIsTemp
&&
391 uploadMode
== Upload_StagingPooled
) {
392 static const uint32_t kMaxPoolSize
= 5;
393 if (mOwner
->mStagingPool
.Length() < kMaxPoolSize
) {
394 mOwner
->mStagingPool
.AppendElement(
395 mRenderCompositor
->mCurrentStagingTexture
);
399 mRenderCompositor
->mCurrentStagingTexture
= nullptr;
400 mRenderCompositor
->mCurrentStagingTextureIsTemp
= false;
403 bool RenderCompositorD3D11SWGL::TileD3D11::IsValid() { return !!mTexture
; }
405 already_AddRefed
<ID3D11Texture2D
>
406 RenderCompositorD3D11SWGL::CreateStagingTexture(const gfx::IntSize aSize
) {
407 CD3D11_TEXTURE2D_DESC
desc(DXGI_FORMAT_B8G8R8A8_UNORM
, aSize
.width
,
410 desc
.CPUAccessFlags
= D3D11_CPU_ACCESS_WRITE
| D3D11_CPU_ACCESS_READ
;
411 desc
.Usage
= D3D11_USAGE_STAGING
;
414 RefPtr
<ID3D11Texture2D
> cpuTexture
;
415 DebugOnly
<HRESULT
> hr
=
416 GetDevice()->CreateTexture2D(&desc
, nullptr, getter_AddRefs(cpuTexture
));
417 MOZ_ASSERT(SUCCEEDED(hr
));
419 gfxCriticalNote
<< "Failed to create StagingTexture: " << aSize
;
420 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
422 return cpuTexture
.forget();
425 already_AddRefed
<gfx::DataSourceSurface
>
426 RenderCompositorD3D11SWGL::CreateStagingSurface(const gfx::IntSize aSize
) {
427 return gfx::Factory::CreateDataSourceSurface(aSize
,
428 gfx::SurfaceFormat::B8G8R8A8
);
431 UniquePtr
<RenderCompositorLayersSWGL::Tile
>
432 RenderCompositorD3D11SWGL::DoCreateTile(Surface
* aSurface
) {
433 MOZ_RELEASE_ASSERT(aSurface
);
435 const auto tileSize
= aSurface
->TileSize();
437 if (mUploadMode
== Upload_Immediate
) {
438 RefPtr
<DataTextureSourceD3D11
> source
=
439 new DataTextureSourceD3D11(gfx::SurfaceFormat::B8G8R8A8
, mCompositor
,
440 layers::TextureFlags::NO_FLAGS
);
441 RefPtr
<gfx::DataSourceSurface
> surf
= CreateStagingSurface(tileSize
);
442 return MakeUnique
<TileD3D11
>(source
, nullptr, surf
, aSurface
, this);
445 MOZ_ASSERT(mUploadMode
== Upload_Staging
||
446 mUploadMode
== Upload_StagingNoBlock
||
447 mUploadMode
== Upload_StagingPooled
);
449 CD3D11_TEXTURE2D_DESC
desc(DXGI_FORMAT_B8G8R8A8_UNORM
, tileSize
.width
,
450 tileSize
.height
, 1, 1);
452 RefPtr
<ID3D11Texture2D
> texture
;
453 DebugOnly
<HRESULT
> hr
=
454 GetDevice()->CreateTexture2D(&desc
, nullptr, getter_AddRefs(texture
));
455 MOZ_ASSERT(SUCCEEDED(hr
));
457 gfxCriticalNote
<< "Failed to allocate Texture2D: " << aSurface
->TileSize();
458 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
459 return MakeUnique
<TileD3D11
>(nullptr, nullptr, nullptr, aSurface
, this);
462 RefPtr
<DataTextureSourceD3D11
> source
= new DataTextureSourceD3D11(
463 GetDevice(), gfx::SurfaceFormat::B8G8R8A8
, texture
);
465 RefPtr
<ID3D11Texture2D
> cpuTexture
= CreateStagingTexture(tileSize
);
466 return MakeUnique
<TileD3D11
>(source
, cpuTexture
, nullptr, aSurface
, this);
469 bool RenderCompositorD3D11SWGL::MaybeReadback(
470 const gfx::IntSize
& aReadbackSize
, const wr::ImageFormat
& aReadbackFormat
,
471 const Range
<uint8_t>& aReadbackBuffer
, bool* aNeedsYFlip
) {
472 MOZ_ASSERT(aReadbackFormat
== wr::ImageFormat::BGRA8
);
475 aReadbackSize
.width
* gfx::BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8
);
476 RefPtr
<gfx::DrawTarget
> dt
= gfx::Factory::CreateDrawTargetForData(
477 gfx::BackendType::SKIA
, &aReadbackBuffer
[0], aReadbackSize
, stride
,
478 gfx::SurfaceFormat::B8G8R8A8
, false);
483 GetCompositorD3D11()->Readback(dt
);
488 } // namespace mozilla