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/TextureD3D11.h"
15 #include "mozilla/layers/Effects.h"
16 #include "mozilla/webrender/RenderD3D11TextureHost.h"
17 #include "RenderCompositorRecordedFrame.h"
18 #include "RenderThread.h"
21 using namespace layers
;
25 extern LazyLogModule gRenderThreadLog
;
26 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
28 RenderCompositorD3D11SWGL::UploadMode
29 RenderCompositorD3D11SWGL::GetUploadMode() {
30 int mode
= StaticPrefs::gfx_webrender_software_d3d11_upload_mode();
33 return Upload_Immediate
;
35 return Upload_Staging
;
37 return Upload_StagingNoBlock
;
39 return Upload_StagingPooled
;
41 return Upload_Staging
;
45 UniquePtr
<RenderCompositor
> RenderCompositorD3D11SWGL::Create(
46 const RefPtr
<widget::CompositorWidget
>& aWidget
, nsACString
& aError
) {
47 if (!aWidget
->GetCompositorOptions().AllowSoftwareWebRenderD3D11() ||
48 !gfx::gfxConfig::IsEnabled(gfx::Feature::D3D11_COMPOSITING
)) {
52 void* ctx
= wr_swgl_create_context();
54 gfxCriticalNote
<< "Failed SWGL context creation for WebRender";
58 RefPtr
<CompositorD3D11
> compositor
= MakeAndAddRef
<CompositorD3D11
>(aWidget
);
60 if (!compositor
->Initialize(&log
)) {
61 gfxCriticalNote
<< "Failed to initialize CompositorD3D11 for SWGL: "
65 return MakeUnique
<RenderCompositorD3D11SWGL
>(compositor
, aWidget
, ctx
);
68 RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL(
69 CompositorD3D11
* aCompositor
,
70 const RefPtr
<widget::CompositorWidget
>& aWidget
, void* aContext
)
71 : RenderCompositorLayersSWGL(aCompositor
, aWidget
, aContext
) {
72 LOG("RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL()");
74 mSyncObject
= GetCompositorD3D11()->GetSyncObject();
77 RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL() {
78 LOG("RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL()");
81 bool RenderCompositorD3D11SWGL::BeginFrame() {
82 if (!RenderCompositorLayersSWGL::BeginFrame()) {
86 mUploadMode
= GetUploadMode();
90 void RenderCompositorD3D11SWGL::HandleExternalImage(
91 RenderTextureHost
* aExternalImage
, FrameSurface
& aFrameSurface
) {
92 // We need to hold the texture source separately from the effect,
93 // since the effect doesn't hold a strong reference.
94 RefPtr
<DataTextureSourceD3D11
> layer
;
95 RefPtr
<TexturedEffect
> texturedEffect
;
97 if (auto* host
= aExternalImage
->AsRenderDXGITextureHost()) {
98 if (!host
->EnsureD3D11Texture2D(GetDevice())) {
102 layer
= new DataTextureSourceD3D11(GetDevice(), host
->GetFormat(),
103 host
->GetD3D11Texture2D());
104 if (host
->GetFormat() == gfx::SurfaceFormat::NV12
||
105 host
->GetFormat() == gfx::SurfaceFormat::P010
||
106 host
->GetFormat() == gfx::SurfaceFormat::P016
) {
107 const auto yuv
= FromYUVRangedColorSpace(host
->GetYUVColorSpace());
109 new EffectNV12(layer
, yuv
.space
, yuv
.range
, host
->GetColorDepth(),
110 aFrameSurface
.mFilter
);
112 MOZ_ASSERT(host
->GetFormat() == gfx::SurfaceFormat::B8G8R8X8
||
113 host
->GetFormat() == gfx::SurfaceFormat::B8G8R8A8
);
114 texturedEffect
= CreateTexturedEffect(host
->GetFormat(), layer
,
115 aFrameSurface
.mFilter
, true);
117 size
= host
->GetSize(0);
118 host
->LockInternal();
119 } else if (auto* host
= aExternalImage
->AsRenderDXGIYCbCrTextureHost()) {
120 if (!host
->EnsureD3D11Texture2D(GetDevice())) {
124 layer
= new DataTextureSourceD3D11(GetDevice(), gfx::SurfaceFormat::A8
,
125 host
->GetD3D11Texture2D(0));
126 RefPtr
<DataTextureSourceD3D11
> u
= new DataTextureSourceD3D11(
127 GetDevice(), gfx::SurfaceFormat::A8
, host
->GetD3D11Texture2D(1));
128 layer
->SetNextSibling(u
);
129 RefPtr
<DataTextureSourceD3D11
> v
= new DataTextureSourceD3D11(
130 GetDevice(), gfx::SurfaceFormat::A8
, host
->GetD3D11Texture2D(2));
131 u
->SetNextSibling(v
);
133 const auto yuv
= FromYUVRangedColorSpace(host
->GetYUVColorSpace());
135 new EffectYCbCr(layer
, yuv
.space
, yuv
.range
, host
->GetColorDepth(),
136 aFrameSurface
.mFilter
);
137 size
= host
->GetSize(0);
138 host
->LockInternal();
141 gfx::Rect
drawRect(0, 0, size
.width
, size
.height
);
144 effect
.mPrimaryEffect
= texturedEffect
;
145 mCompositor
->DrawQuad(drawRect
, aFrameSurface
.mClipRect
, effect
, 1.0,
146 aFrameSurface
.mTransform
, drawRect
);
148 if (auto* host
= aExternalImage
->AsRenderDXGITextureHost()) {
150 } else if (auto* host
= aExternalImage
->AsRenderDXGIYCbCrTextureHost()) {
155 void RenderCompositorD3D11SWGL::Pause() {}
157 bool RenderCompositorD3D11SWGL::Resume() { return true; }
159 gfx::DeviceResetReason
RenderCompositorD3D11SWGL::IsContextLost(bool aForce
) {
160 // CompositorD3D11 uses ID3D11Device for composite. The device status needs to
162 auto reason
= GetDevice()->GetDeviceRemovedReason();
163 return layers::DXGIErrorToDeviceResetReason(reason
);
166 UniquePtr
<RenderCompositorLayersSWGL::Surface
>
167 RenderCompositorD3D11SWGL::DoCreateSurface(wr::DeviceIntSize aTileSize
,
169 return MakeUnique
<SurfaceD3D11SWGL
>(aTileSize
, aIsOpaque
);
172 SurfaceD3D11SWGL::SurfaceD3D11SWGL(wr::DeviceIntSize aTileSize
, bool aIsOpaque
)
173 : Surface(aTileSize
, aIsOpaque
) {}
175 RenderCompositorD3D11SWGL::TileD3D11::TileD3D11(
176 layers::DataTextureSourceD3D11
* aTexture
, ID3D11Texture2D
* aStagingTexture
,
177 gfx::DataSourceSurface
* aDataSourceSurface
, Surface
* aOwner
,
178 RenderCompositorD3D11SWGL
* aRenderCompositor
)
181 mStagingTexture(aStagingTexture
),
182 mSurface(aDataSourceSurface
),
183 mOwner(aOwner
->AsSurfaceD3D11SWGL()),
184 mRenderCompositor(aRenderCompositor
) {}
186 bool RenderCompositorD3D11SWGL::TileD3D11::Map(wr::DeviceIntRect aDirtyRect
,
187 wr::DeviceIntRect aValidRect
,
188 void** aData
, int32_t* aStride
) {
189 const UploadMode uploadMode
= mRenderCompositor
->GetUploadMode();
190 const gfx::IntSize tileSize
= mOwner
->TileSize();
196 // Check if this tile's upload method matches what we're using for this frame,
197 // and if not then reallocate to fix it. Do this before we copy the struct
198 // into mCurrentTile.
199 if (uploadMode
== Upload_Immediate
) {
200 if (mStagingTexture
) {
201 MOZ_ASSERT(!mSurface
);
202 mStagingTexture
= nullptr;
203 mSurface
= mRenderCompositor
->CreateStagingSurface(tileSize
);
207 MOZ_ASSERT(!mStagingTexture
);
209 mStagingTexture
= mRenderCompositor
->CreateStagingTexture(tileSize
);
213 mRenderCompositor
->mCurrentStagingTexture
= mStagingTexture
;
214 mRenderCompositor
->mCurrentStagingTextureIsTemp
= false;
216 if (uploadMode
== Upload_Immediate
) {
217 gfx::DataSourceSurface::MappedSurface map
;
218 if (!mSurface
->Map(gfx::DataSourceSurface::READ_WRITE
, &map
)) {
222 *aData
= map
.mData
+ aValidRect
.min
.y
* map
.mStride
+ aValidRect
.min
.x
* 4;
223 *aStride
= map
.mStride
;
224 // Ensure our mapped data is accessible by writing to the beginning and end
225 // of the dirty region. See bug 1717519
226 if (aDirtyRect
.width() > 0 && aDirtyRect
.height() > 0) {
227 uint32_t* probeData
= (uint32_t*)map
.mData
+
228 aDirtyRect
.min
.y
* (map
.mStride
/ 4) +
231 uint32_t* probeDataEnd
= (uint32_t*)map
.mData
+
232 (aDirtyRect
.max
.y
- 1) * (map
.mStride
/ 4) +
233 (aDirtyRect
.max
.x
- 1);
237 mValidRect
= gfx::Rect(aValidRect
.min
.x
, aValidRect
.min
.y
,
238 aValidRect
.width(), aValidRect
.height());
242 if (!mRenderCompositor
->mCurrentStagingTexture
) {
246 RefPtr
<ID3D11DeviceContext
> context
;
247 mRenderCompositor
->GetDevice()->GetImmediateContext(getter_AddRefs(context
));
249 D3D11_MAPPED_SUBRESOURCE mappedSubresource
;
251 bool shouldBlock
= uploadMode
== Upload_Staging
;
253 HRESULT hr
= context
->Map(
254 mRenderCompositor
->mCurrentStagingTexture
, 0, D3D11_MAP_READ_WRITE
,
255 shouldBlock
? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT
, &mappedSubresource
);
256 if (hr
== DXGI_ERROR_WAS_STILL_DRAWING
) {
257 // mCurrentTile is a copy of the real tile data, so we can just replace the
258 // staging one with a temporary for this draw. The staging texture for the
259 // real tile remains untouched, so we'll go back to using that for future
260 // frames and discard the new one. In the future we could improve this by
261 // having a pool of shared staging textures for all the tiles.
263 // Mark the tile as having a temporary staging texture.
264 mRenderCompositor
->mCurrentStagingTextureIsTemp
= true;
266 // Try grabbing a texture from the staging pool and see if we can use that.
267 if (uploadMode
== Upload_StagingPooled
&& mOwner
->mStagingPool
.Length()) {
268 mRenderCompositor
->mCurrentStagingTexture
=
269 mOwner
->mStagingPool
.ElementAt(0);
270 mOwner
->mStagingPool
.RemoveElementAt(0);
271 hr
= context
->Map(mRenderCompositor
->mCurrentStagingTexture
, 0,
272 D3D11_MAP_READ_WRITE
, D3D11_MAP_FLAG_DO_NOT_WAIT
,
275 // If that failed, put it back into the pool (but at the end).
276 if (hr
== DXGI_ERROR_WAS_STILL_DRAWING
) {
277 mOwner
->mStagingPool
.AppendElement(
278 mRenderCompositor
->mCurrentStagingTexture
);
282 // No staging textures, or we tried one and it was busy. Allocate a brand
284 if (hr
== DXGI_ERROR_WAS_STILL_DRAWING
) {
285 mRenderCompositor
->mCurrentStagingTexture
=
286 mRenderCompositor
->CreateStagingTexture(tileSize
);
287 if (!mRenderCompositor
->mCurrentStagingTexture
) {
290 hr
= context
->Map(mRenderCompositor
->mCurrentStagingTexture
, 0,
291 D3D11_MAP_READ_WRITE
, 0, &mappedSubresource
);
294 if (!SUCCEEDED(hr
)) {
295 gfxCriticalError() << "Failed to map tile: " << gfx::hexa(hr
);
296 // This is only expected to fail if we hit a device reset.
298 mRenderCompositor
->GetDevice()->GetDeviceRemovedReason() != S_OK
);
302 // aData is expected to contain a pointer to the first pixel within the valid
303 // rect, so take the mapped resource's data (which covers the full tile size)
304 // and offset it by the top/left of the valid rect.
305 *aData
= (uint8_t*)mappedSubresource
.pData
+
306 aValidRect
.min
.y
* mappedSubresource
.RowPitch
+ aValidRect
.min
.x
* 4;
307 *aStride
= mappedSubresource
.RowPitch
;
309 // Ensure our mapped data is accessible by writing to the beginning and end
310 // of the dirty region. See bug 1717519
311 if (aDirtyRect
.width() > 0 && aDirtyRect
.height() > 0) {
312 uint32_t* probeData
= (uint32_t*)mappedSubresource
.pData
+
313 aDirtyRect
.min
.y
* (mappedSubresource
.RowPitch
/ 4) +
316 uint32_t* probeDataEnd
=
317 (uint32_t*)mappedSubresource
.pData
+
318 (aDirtyRect
.max
.y
- 1) * (mappedSubresource
.RowPitch
/ 4) +
319 (aDirtyRect
.max
.x
- 1);
323 // Store the new valid rect, so that we can composite only those pixels
324 mValidRect
= gfx::Rect(aValidRect
.min
.x
, aValidRect
.min
.y
, aValidRect
.width(),
325 aValidRect
.height());
330 void RenderCompositorD3D11SWGL::TileD3D11::Unmap(
331 const gfx::IntRect
& aDirtyRect
) {
332 const UploadMode uploadMode
= mRenderCompositor
->GetUploadMode();
339 MOZ_ASSERT(uploadMode
== Upload_Immediate
);
341 nsIntRegion
dirty(aDirtyRect
);
342 // This uses UpdateSubresource, which blocks, so is likely implemented as a
343 // memcpy into driver owned memory, followed by an async upload. The staging
344 // method should avoid this extra copy, and is likely to be faster usually.
345 // We could possible do this call on a background thread so that sw-wr can
346 // start drawing the next tile while the memcpy is in progress.
347 mTexture
->Update(mSurface
, &dirty
);
351 if (!mRenderCompositor
->mCurrentStagingTexture
) {
355 RefPtr
<ID3D11DeviceContext
> context
;
356 mRenderCompositor
->GetDevice()->GetImmediateContext(getter_AddRefs(context
));
358 context
->Unmap(mRenderCompositor
->mCurrentStagingTexture
, 0);
363 box
.left
= aDirtyRect
.X();
364 box
.top
= aDirtyRect
.Y();
365 box
.right
= aDirtyRect
.XMost();
366 box
.bottom
= aDirtyRect
.YMost();
368 context
->CopySubresourceRegion(
369 mTexture
->GetD3D11Texture(), 0, aDirtyRect
.x
, aDirtyRect
.y
, 0,
370 mRenderCompositor
->mCurrentStagingTexture
, 0, &box
);
372 // If we allocated a temp staging texture for this tile, and we're running
373 // in pooled mode, then consider adding it to the pool for later.
374 if (mRenderCompositor
->mCurrentStagingTextureIsTemp
&&
375 uploadMode
== Upload_StagingPooled
) {
376 static const uint32_t kMaxPoolSize
= 5;
377 if (mOwner
->mStagingPool
.Length() < kMaxPoolSize
) {
378 mOwner
->mStagingPool
.AppendElement(
379 mRenderCompositor
->mCurrentStagingTexture
);
383 mRenderCompositor
->mCurrentStagingTexture
= nullptr;
384 mRenderCompositor
->mCurrentStagingTextureIsTemp
= false;
387 bool RenderCompositorD3D11SWGL::TileD3D11::IsValid() { return !!mTexture
; }
389 already_AddRefed
<ID3D11Texture2D
>
390 RenderCompositorD3D11SWGL::CreateStagingTexture(const gfx::IntSize aSize
) {
391 CD3D11_TEXTURE2D_DESC
desc(DXGI_FORMAT_B8G8R8A8_UNORM
, aSize
.width
,
394 desc
.CPUAccessFlags
= D3D11_CPU_ACCESS_WRITE
| D3D11_CPU_ACCESS_READ
;
395 desc
.Usage
= D3D11_USAGE_STAGING
;
398 RefPtr
<ID3D11Texture2D
> cpuTexture
;
399 DebugOnly
<HRESULT
> hr
=
400 GetDevice()->CreateTexture2D(&desc
, nullptr, getter_AddRefs(cpuTexture
));
401 MOZ_ASSERT(SUCCEEDED(hr
));
403 gfxCriticalNote
<< "Failed to create StagingTexture: " << aSize
;
404 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
406 return cpuTexture
.forget();
409 already_AddRefed
<gfx::DataSourceSurface
>
410 RenderCompositorD3D11SWGL::CreateStagingSurface(const gfx::IntSize aSize
) {
411 return gfx::Factory::CreateDataSourceSurface(aSize
,
412 gfx::SurfaceFormat::B8G8R8A8
);
415 UniquePtr
<RenderCompositorLayersSWGL::Tile
>
416 RenderCompositorD3D11SWGL::DoCreateTile(Surface
* aSurface
) {
417 MOZ_RELEASE_ASSERT(aSurface
);
419 const auto tileSize
= aSurface
->TileSize();
421 if (mUploadMode
== Upload_Immediate
) {
422 RefPtr
<DataTextureSourceD3D11
> source
=
423 new DataTextureSourceD3D11(gfx::SurfaceFormat::B8G8R8A8
, mCompositor
,
424 layers::TextureFlags::NO_FLAGS
);
425 RefPtr
<gfx::DataSourceSurface
> surf
= CreateStagingSurface(tileSize
);
426 return MakeUnique
<TileD3D11
>(source
, nullptr, surf
, aSurface
, this);
429 MOZ_ASSERT(mUploadMode
== Upload_Staging
||
430 mUploadMode
== Upload_StagingNoBlock
||
431 mUploadMode
== Upload_StagingPooled
);
433 CD3D11_TEXTURE2D_DESC
desc(DXGI_FORMAT_B8G8R8A8_UNORM
, tileSize
.width
,
434 tileSize
.height
, 1, 1);
436 RefPtr
<ID3D11Texture2D
> texture
;
437 DebugOnly
<HRESULT
> hr
=
438 GetDevice()->CreateTexture2D(&desc
, nullptr, getter_AddRefs(texture
));
439 MOZ_ASSERT(SUCCEEDED(hr
));
441 gfxCriticalNote
<< "Failed to allocate Texture2D: " << aSurface
->TileSize();
442 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
443 return MakeUnique
<TileD3D11
>(nullptr, nullptr, nullptr, aSurface
, this);
446 RefPtr
<DataTextureSourceD3D11
> source
= new DataTextureSourceD3D11(
447 GetDevice(), gfx::SurfaceFormat::B8G8R8A8
, texture
);
449 RefPtr
<ID3D11Texture2D
> cpuTexture
= CreateStagingTexture(tileSize
);
450 return MakeUnique
<TileD3D11
>(source
, cpuTexture
, nullptr, aSurface
, this);
453 bool RenderCompositorD3D11SWGL::MaybeReadback(
454 const gfx::IntSize
& aReadbackSize
, const wr::ImageFormat
& aReadbackFormat
,
455 const Range
<uint8_t>& aReadbackBuffer
, bool* aNeedsYFlip
) {
456 MOZ_ASSERT(aReadbackFormat
== wr::ImageFormat::BGRA8
);
459 aReadbackSize
.width
* gfx::BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8
);
460 RefPtr
<gfx::DrawTarget
> dt
= gfx::Factory::CreateDrawTargetForData(
461 gfx::BackendType::SKIA
, &aReadbackBuffer
[0], aReadbackSize
, stride
,
462 gfx::SurfaceFormat::B8G8R8A8
, false);
467 GetCompositorD3D11()->Readback(dt
);
472 } // namespace mozilla