Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / gfx / webrender_bindings / RenderCompositorD3D11SWGL.cpp
blobd95074e90148c8bb0678cf8087c322b9e1e717fd
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/Effects.h"
15 #include "mozilla/webrender/RenderD3D11TextureHost.h"
16 #include "RenderCompositorRecordedFrame.h"
17 #include "RenderThread.h"
19 namespace mozilla {
20 using namespace layers;
22 namespace wr {
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();
30 switch (mode) {
31 case 1:
32 return Upload_Immediate;
33 case 2:
34 return Upload_Staging;
35 case 3:
36 return Upload_StagingNoBlock;
37 case 4:
38 return Upload_StagingPooled;
39 default:
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)) {
48 return nullptr;
51 void* ctx = wr_swgl_create_context();
52 if (!ctx) {
53 gfxCriticalNote << "Failed SWGL context creation for WebRender";
54 return nullptr;
57 RefPtr<CompositorD3D11> compositor = MakeAndAddRef<CompositorD3D11>(aWidget);
58 nsCString log;
59 if (!compositor->Initialize(&log)) {
60 gfxCriticalNote << "Failed to initialize CompositorD3D11 for SWGL: "
61 << log.get();
62 return nullptr;
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()) {
82 return false;
85 mUploadMode = GetUploadMode();
86 return true;
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;
95 gfx::IntSize size;
96 if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
97 if (!host->EnsureD3D11Texture2D(GetDevice())) {
98 return;
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());
107 texturedEffect =
108 new EffectNV12(layer, yuv.space, yuv.range, host->GetColorDepth(),
109 aFrameSurface.mFilter);
110 } else {
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())) {
120 return;
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());
133 texturedEffect =
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);
142 EffectChain effect;
143 effect.mPrimaryEffect = texturedEffect;
144 mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0,
145 aFrameSurface.mTransform, drawRect);
147 if (auto* host = aExternalImage->AsRenderDXGITextureHost()) {
148 host->Unlock();
149 } else if (auto* host = aExternalImage->AsRenderDXGIYCbCrTextureHost()) {
150 host->Unlock();
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
160 // be checked.
161 auto reason = GetDevice()->GetDeviceRemovedReason();
162 switch (reason) {
163 case S_OK:
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;
175 default:
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,
184 bool aIsOpaque) {
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)
195 : Tile(),
196 mTexture(aTexture),
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();
208 if (!IsValid()) {
209 return false;
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);
221 } else {
222 if (mSurface) {
223 MOZ_ASSERT(!mStagingTexture);
224 mSurface = nullptr;
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)) {
235 return false;
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) +
245 aDirtyRect.min.x;
246 *probeData = 0;
247 uint32_t* probeDataEnd = (uint32_t*)map.mData +
248 (aDirtyRect.max.y - 1) * (map.mStride / 4) +
249 (aDirtyRect.max.x - 1);
250 *probeDataEnd = 0;
253 mValidRect = gfx::Rect(aValidRect.min.x, aValidRect.min.y,
254 aValidRect.width(), aValidRect.height());
255 return true;
258 if (!mRenderCompositor->mCurrentStagingTexture) {
259 return false;
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,
289 &mappedSubresource);
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
299 // new one instead.
300 if (hr == DXGI_ERROR_WAS_STILL_DRAWING) {
301 mRenderCompositor->mCurrentStagingTexture =
302 mRenderCompositor->CreateStagingTexture(tileSize);
303 if (!mRenderCompositor->mCurrentStagingTexture) {
304 return false;
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.
313 MOZ_RELEASE_ASSERT(
314 mRenderCompositor->GetDevice()->GetDeviceRemovedReason() != S_OK);
315 return false;
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) +
330 aDirtyRect.min.x;
331 *probeData = 0;
332 uint32_t* probeDataEnd =
333 (uint32_t*)mappedSubresource.pData +
334 (aDirtyRect.max.y - 1) * (mappedSubresource.RowPitch / 4) +
335 (aDirtyRect.max.x - 1);
336 *probeDataEnd = 0;
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());
343 return true;
346 void RenderCompositorD3D11SWGL::TileD3D11::Unmap(
347 const gfx::IntRect& aDirtyRect) {
348 const UploadMode uploadMode = mRenderCompositor->GetUploadMode();
350 if (!IsValid()) {
351 return;
354 if (mSurface) {
355 MOZ_ASSERT(uploadMode == Upload_Immediate);
356 mSurface->Unmap();
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);
364 return;
367 if (!mRenderCompositor->mCurrentStagingTexture) {
368 return;
371 RefPtr<ID3D11DeviceContext> context;
372 mRenderCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context));
374 context->Unmap(mRenderCompositor->mCurrentStagingTexture, 0);
376 D3D11_BOX box;
377 box.front = 0;
378 box.back = 1;
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,
408 aSize.height, 1, 1);
410 desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
411 desc.Usage = D3D11_USAGE_STAGING;
412 desc.BindFlags = 0;
414 RefPtr<ID3D11Texture2D> cpuTexture;
415 DebugOnly<HRESULT> hr =
416 GetDevice()->CreateTexture2D(&desc, nullptr, getter_AddRefs(cpuTexture));
417 MOZ_ASSERT(SUCCEEDED(hr));
418 if (!cpuTexture) {
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));
456 if (!texture) {
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);
474 auto stride =
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);
479 if (!dt) {
480 return false;
483 GetCompositorD3D11()->Readback(dt);
484 return true;
487 } // namespace wr
488 } // namespace mozilla