Bug 1909074. Don't pass OFFSET_BY_ORIGIN to GetResultingTransformMatrix when it's...
[gecko.git] / gfx / webrender_bindings / RenderCompositorD3D11SWGL.cpp
blob6ed7e59faae554731504c978d8abe048c15af5a1
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 "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"
20 namespace mozilla {
21 using namespace layers;
23 namespace wr {
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();
31 switch (mode) {
32 case 1:
33 return Upload_Immediate;
34 case 2:
35 return Upload_Staging;
36 case 3:
37 return Upload_StagingNoBlock;
38 case 4:
39 return Upload_StagingPooled;
40 default:
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)) {
49 return nullptr;
52 void* ctx = wr_swgl_create_context();
53 if (!ctx) {
54 gfxCriticalNote << "Failed SWGL context creation for WebRender";
55 return nullptr;
58 RefPtr<CompositorD3D11> compositor = MakeAndAddRef<CompositorD3D11>(aWidget);
59 nsCString log;
60 if (!compositor->Initialize(&log)) {
61 gfxCriticalNote << "Failed to initialize CompositorD3D11 for SWGL: "
62 << log.get();
63 return nullptr;
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()) {
83 return false;
86 mUploadMode = GetUploadMode();
87 return true;
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;
96 gfx::IntSize size;
97 if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
98 if (!host->EnsureD3D11Texture2D(GetDevice())) {
99 return;
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());
108 texturedEffect =
109 new EffectNV12(layer, yuv.space, yuv.range, host->GetColorDepth(),
110 aFrameSurface.mFilter);
111 } else {
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())) {
121 return;
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());
134 texturedEffect =
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);
143 EffectChain effect;
144 effect.mPrimaryEffect = texturedEffect;
145 mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0,
146 aFrameSurface.mTransform, drawRect);
148 if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
149 host->Unlock();
150 } else if (auto* host = aExternalImage->AsRenderDXGIYCbCrTextureHost()) {
151 host->Unlock();
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
161 // be checked.
162 auto reason = GetDevice()->GetDeviceRemovedReason();
163 return layers::DXGIErrorToDeviceResetReason(reason);
166 UniquePtr<RenderCompositorLayersSWGL::Surface>
167 RenderCompositorD3D11SWGL::DoCreateSurface(wr::DeviceIntSize aTileSize,
168 bool aIsOpaque) {
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)
179 : Tile(),
180 mTexture(aTexture),
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();
192 if (!IsValid()) {
193 return false;
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);
205 } else {
206 if (mSurface) {
207 MOZ_ASSERT(!mStagingTexture);
208 mSurface = nullptr;
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)) {
219 return false;
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) +
229 aDirtyRect.min.x;
230 *probeData = 0;
231 uint32_t* probeDataEnd = (uint32_t*)map.mData +
232 (aDirtyRect.max.y - 1) * (map.mStride / 4) +
233 (aDirtyRect.max.x - 1);
234 *probeDataEnd = 0;
237 mValidRect = gfx::Rect(aValidRect.min.x, aValidRect.min.y,
238 aValidRect.width(), aValidRect.height());
239 return true;
242 if (!mRenderCompositor->mCurrentStagingTexture) {
243 return false;
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,
273 &mappedSubresource);
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
283 // new one instead.
284 if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
285 mRenderCompositor->mCurrentStagingTexture =
286 mRenderCompositor->CreateStagingTexture(tileSize);
287 if (!mRenderCompositor->mCurrentStagingTexture) {
288 return false;
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.
297 MOZ_RELEASE_ASSERT(
298 mRenderCompositor->GetDevice()->GetDeviceRemovedReason() != S_OK);
299 return false;
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) +
314 aDirtyRect.min.x;
315 *probeData = 0;
316 uint32_t* probeDataEnd =
317 (uint32_t*)mappedSubresource.pData +
318 (aDirtyRect.max.y - 1) * (mappedSubresource.RowPitch / 4) +
319 (aDirtyRect.max.x - 1);
320 *probeDataEnd = 0;
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());
327 return true;
330 void RenderCompositorD3D11SWGL::TileD3D11::Unmap(
331 const gfx::IntRect& aDirtyRect) {
332 const UploadMode uploadMode = mRenderCompositor->GetUploadMode();
334 if (!IsValid()) {
335 return;
338 if (mSurface) {
339 MOZ_ASSERT(uploadMode == Upload_Immediate);
340 mSurface->Unmap();
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);
348 return;
351 if (!mRenderCompositor->mCurrentStagingTexture) {
352 return;
355 RefPtr<ID3D11DeviceContext> context;
356 mRenderCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context));
358 context->Unmap(mRenderCompositor->mCurrentStagingTexture, 0);
360 D3D11_BOX box;
361 box.front = 0;
362 box.back = 1;
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,
392 aSize.height, 1, 1);
394 desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
395 desc.Usage = D3D11_USAGE_STAGING;
396 desc.BindFlags = 0;
398 RefPtr<ID3D11Texture2D> cpuTexture;
399 DebugOnly<HRESULT> hr =
400 GetDevice()->CreateTexture2D(&desc, nullptr, getter_AddRefs(cpuTexture));
401 MOZ_ASSERT(SUCCEEDED(hr));
402 if (!cpuTexture) {
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));
440 if (!texture) {
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);
458 auto stride =
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);
463 if (!dt) {
464 return false;
467 GetCompositorD3D11()->Readback(dt);
468 return true;
471 } // namespace wr
472 } // namespace mozilla