RenderCompositorD3D11SWGL.cpp (17651B)
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 4 * 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/. */ 8 9 #include "RenderCompositorD3D11SWGL.h" 10 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" 19 20 namespace mozilla { 21 using namespace layers; 22 23 namespace wr { 24 25 extern LazyLogModule gRenderThreadLog; 26 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__)) 27 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; 42 } 43 } 44 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; 50 } 51 52 void* ctx = wr_swgl_create_context(); 53 if (!ctx) { 54 gfxCriticalNote << "Failed SWGL context creation for WebRender"; 55 return nullptr; 56 } 57 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; 64 } 65 return MakeUnique<RenderCompositorD3D11SWGL>(compositor, aWidget, ctx); 66 } 67 68 RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL( 69 CompositorD3D11* aCompositor, 70 const RefPtr<widget::CompositorWidget>& aWidget, void* aContext) 71 : RenderCompositorLayersSWGL(aCompositor, aWidget, aContext) { 72 LOG("RenderCompositorD3D11SWGL::RenderCompositorD3D11SWGL()"); 73 74 mSyncObject = GetCompositorD3D11()->GetSyncObject(); 75 } 76 77 RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL() { 78 LOG("RenderCompositorD3D11SWGL::~RenderCompositorD3D11SWGL()"); 79 } 80 81 bool RenderCompositorD3D11SWGL::BeginFrame() { 82 if (!RenderCompositorLayersSWGL::BeginFrame()) { 83 return false; 84 } 85 86 mUploadMode = GetUploadMode(); 87 return true; 88 } 89 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; 100 } 101 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); 116 } 117 size = host->GetSize(0); 118 host->LockInternal(); 119 } else if (auto* host = aExternalImage->AsRenderDXGIYCbCrTextureHost()) { 120 if (!host->EnsureD3D11Texture2D(GetDevice())) { 121 return; 122 } 123 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); 132 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(); 139 } 140 141 gfx::Rect drawRect(0, 0, size.width, size.height); 142 143 EffectChain effect; 144 effect.mPrimaryEffect = texturedEffect; 145 mCompositor->DrawQuad(drawRect, aFrameSurface.mClipRect, effect, 1.0, 146 aFrameSurface.mTransform, drawRect); 147 148 if (auto* host = aExternalImage->AsRenderDXGITextureHost()) { 149 host->Unlock(); 150 } else if (auto* host = aExternalImage->AsRenderDXGIYCbCrTextureHost()) { 151 host->Unlock(); 152 } 153 } 154 155 void RenderCompositorD3D11SWGL::Pause() {} 156 157 bool RenderCompositorD3D11SWGL::Resume() { return true; } 158 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); 164 } 165 166 UniquePtr<RenderCompositorLayersSWGL::Surface> 167 RenderCompositorD3D11SWGL::DoCreateSurface(wr::DeviceIntSize aTileSize, 168 bool aIsOpaque) { 169 return MakeUnique<SurfaceD3D11SWGL>(aTileSize, aIsOpaque); 170 } 171 172 SurfaceD3D11SWGL::SurfaceD3D11SWGL(wr::DeviceIntSize aTileSize, bool aIsOpaque) 173 : Surface(aTileSize, aIsOpaque) {} 174 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) {} 185 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(); 191 192 if (!IsValid()) { 193 return false; 194 } 195 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); 204 } 205 } else { 206 if (mSurface) { 207 MOZ_ASSERT(!mStagingTexture); 208 mSurface = nullptr; 209 mStagingTexture = mRenderCompositor->CreateStagingTexture(tileSize); 210 } 211 } 212 213 mRenderCompositor->mCurrentStagingTexture = mStagingTexture; 214 mRenderCompositor->mCurrentStagingTextureIsTemp = false; 215 216 if (uploadMode == Upload_Immediate) { 217 gfx::DataSourceSurface::MappedSurface map; 218 if (!mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) { 219 return false; 220 } 221 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; 235 } 236 237 mValidRect = gfx::Rect(aValidRect.min.x, aValidRect.min.y, 238 aValidRect.width(), aValidRect.height()); 239 return true; 240 } 241 242 if (!mRenderCompositor->mCurrentStagingTexture) { 243 return false; 244 } 245 246 RefPtr<ID3D11DeviceContext> context; 247 mRenderCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context)); 248 249 D3D11_MAPPED_SUBRESOURCE mappedSubresource; 250 251 bool shouldBlock = uploadMode == Upload_Staging; 252 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. 262 263 // Mark the tile as having a temporary staging texture. 264 mRenderCompositor->mCurrentStagingTextureIsTemp = true; 265 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); 274 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); 279 } 280 } 281 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; 289 } 290 hr = context->Map(mRenderCompositor->mCurrentStagingTexture, 0, 291 D3D11_MAP_READ_WRITE, 0, &mappedSubresource); 292 } 293 } 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; 300 } 301 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; 308 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; 321 } 322 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()); 326 327 return true; 328 } 329 330 void RenderCompositorD3D11SWGL::TileD3D11::Unmap( 331 const gfx::IntRect& aDirtyRect) { 332 const UploadMode uploadMode = mRenderCompositor->GetUploadMode(); 333 334 if (!IsValid()) { 335 return; 336 } 337 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; 349 } 350 351 if (!mRenderCompositor->mCurrentStagingTexture) { 352 return; 353 } 354 355 RefPtr<ID3D11DeviceContext> context; 356 mRenderCompositor->GetDevice()->GetImmediateContext(getter_AddRefs(context)); 357 358 context->Unmap(mRenderCompositor->mCurrentStagingTexture, 0); 359 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(); 367 368 context->CopySubresourceRegion( 369 mTexture->GetD3D11Texture(), 0, aDirtyRect.x, aDirtyRect.y, 0, 370 mRenderCompositor->mCurrentStagingTexture, 0, &box); 371 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); 380 } 381 } 382 383 mRenderCompositor->mCurrentStagingTexture = nullptr; 384 mRenderCompositor->mCurrentStagingTextureIsTemp = false; 385 } 386 387 bool RenderCompositorD3D11SWGL::TileD3D11::IsValid() { return !!mTexture; } 388 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); 393 394 desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ; 395 desc.Usage = D3D11_USAGE_STAGING; 396 desc.BindFlags = 0; 397 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); 405 } 406 return cpuTexture.forget(); 407 } 408 409 already_AddRefed<gfx::DataSourceSurface> 410 RenderCompositorD3D11SWGL::CreateStagingSurface(const gfx::IntSize aSize) { 411 return gfx::Factory::CreateDataSourceSurface(aSize, 412 gfx::SurfaceFormat::B8G8R8A8); 413 } 414 415 UniquePtr<RenderCompositorLayersSWGL::Tile> 416 RenderCompositorD3D11SWGL::DoCreateTile(Surface* aSurface) { 417 MOZ_RELEASE_ASSERT(aSurface); 418 419 const auto tileSize = aSurface->TileSize(); 420 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); 427 } 428 429 MOZ_ASSERT(mUploadMode == Upload_Staging || 430 mUploadMode == Upload_StagingNoBlock || 431 mUploadMode == Upload_StagingPooled); 432 433 CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, tileSize.width, 434 tileSize.height, 1, 1); 435 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); 444 } 445 446 RefPtr<DataTextureSourceD3D11> source = new DataTextureSourceD3D11( 447 GetDevice(), gfx::SurfaceFormat::B8G8R8A8, texture); 448 449 RefPtr<ID3D11Texture2D> cpuTexture = CreateStagingTexture(tileSize); 450 return MakeUnique<TileD3D11>(source, cpuTexture, nullptr, aSurface, this); 451 } 452 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); 457 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; 465 } 466 467 GetCompositorD3D11()->Readback(dt); 468 return true; 469 } 470 471 } // namespace wr 472 } // namespace mozilla