Bug 1699062 - Flatten toolkit/themes/*/global/alerts/. r=desktop-theme-reviewers,dao
[gecko.git] / gfx / webrender_bindings / RenderCompositorD3D11SWGL.cpp
blob3e338a461b83937493e90ed0aa9bf30c5ab54e9e
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
6 * file,
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"
17 namespace mozilla {
18 using namespace layers;
20 namespace wr {
22 RenderCompositorD3D11SWGL::UploadMode
23 RenderCompositorD3D11SWGL::GetUploadMode() {
24 int mode = StaticPrefs::gfx_webrender_software_d3d11_upload_mode();
25 switch (mode) {
26 case 1:
27 return Upload_Immediate;
28 case 2:
29 return Upload_Staging;
30 case 3:
31 return Upload_StagingNoBlock;
32 case 4:
33 return Upload_StagingPooled;
34 default:
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)) {
43 return nullptr;
46 void* ctx = wr_swgl_create_context();
47 if (!ctx) {
48 gfxCriticalNote << "Failed SWGL context creation for WebRender";
49 return nullptr;
52 RefPtr<CompositorD3D11> compositor =
53 MakeAndAddRef<CompositorD3D11>(nullptr, aWidget);
54 nsCString log;
55 if (!compositor->Initialize(&log)) {
56 gfxCriticalNote << "Failed to initialize CompositorD3D11 for SWGL: "
57 << log.get();
58 return nullptr;
60 compositor->UseForSoftwareWebRender();
62 return MakeUnique<RenderCompositorD3D11SWGL>(compositor, std::move(aWidget),
63 ctx);
66 RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL(
67 CompositorD3D11* aCompositor, RefPtr<widget::CompositorWidget>&& aWidget,
68 void* aContext)
69 : RenderCompositorLayersSWGL(aCompositor, std::move(aWidget), aContext) {
70 mSyncObject = GetCompositorD3D11()->GetSyncObject();
73 RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL() {}
75 bool RenderCompositorD3D11SWGL::BeginFrame() {
76 if (!RenderCompositorLayersSWGL::BeginFrame()) {
77 return false;
80 mUploadMode = GetUploadMode();
81 return true;
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;
90 gfx::IntSize size;
91 if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
92 if (!host->EnsureD3D11Texture2D(GetDevice())) {
93 return;
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) {
101 texturedEffect =
102 new EffectNV12(layer, host->GetYUVColorSpace(), host->GetColorRange(),
103 host->GetColorDepth(), aFrameSurface.mFilter);
104 } else {
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())) {
114 return;
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);
126 texturedEffect =
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);
135 EffectChain effect;
136 effect.mPrimaryEffect = texturedEffect;
137 mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0,
138 aFrameSurface.mTransform, drawRect);
140 if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
141 host->Unlock();
142 } else if (auto* host = aExternalImage->AsRenderDXGIYCbCrTextureHost()) {
143 host->Unlock();
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
153 // be checked.
154 auto reason = GetDevice()->GetDeviceRemovedReason();
155 switch (reason) {
156 case S_OK:
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;
168 default:
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,
177 bool aIsOpaque) {
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)
188 : Tile(),
189 mTexture(aTexture),
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();
201 if (!IsValid()) {
202 return false;
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);
214 } else {
215 if (mSurface) {
216 MOZ_ASSERT(!mStagingTexture);
217 mSurface = nullptr;
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)) {
228 return false;
231 *aData =
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);
236 return true;
239 if (!mRenderCompositor->mCurrentStagingTexture) {
240 return false;
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,
270 &mappedSubresource);
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
280 // new one instead.
281 if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
282 mRenderCompositor->mCurrentStagingTexture =
283 mRenderCompositor->CreateStagingTexture(tileSize);
284 if (!mRenderCompositor->mCurrentStagingTexture) {
285 return false;
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);
305 return true;
308 void RenderCompositorD3D11SWGL::TileD3D11::Unmap(
309 const gfx::IntRect& aDirtyRect) {
310 const UploadMode uploadMode = mRenderCompositor->GetUploadMode();
312 if (!IsValid()) {
313 return;
316 if (mSurface) {
317 MOZ_ASSERT(uploadMode == Upload_Immediate);
318 mSurface->Unmap();
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);
326 return;
329 if (!mRenderCompositor->mCurrentStagingTexture) {
330 return;
333 RefPtr<ID3D11DeviceContext> context;
334 mRenderCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context));
336 context->Unmap(mRenderCompositor->mCurrentStagingTexture, 0);
338 D3D11_BOX box;
339 box.front = 0;
340 box.back = 1;
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,
370 aSize.height, 1, 1);
372 desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
373 desc.Usage = D3D11_USAGE_STAGING;
374 desc.BindFlags = 0;
376 RefPtr<ID3D11Texture2D> cpuTexture;
377 DebugOnly<HRESULT> hr =
378 GetDevice()->CreateTexture2D(&desc, nullptr, getter_AddRefs(cpuTexture));
379 MOZ_ASSERT(SUCCEEDED(hr));
380 if (!cpuTexture) {
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));
417 if (!texture) {
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);
435 auto stride =
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);
440 if (!dt) {
441 return false;
444 GetCompositorD3D11()->Readback(dt);
445 return true;
448 } // namespace wr
449 } // namespace mozilla