GpuProcessD3D11TextureMap.cpp (13704B)
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 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "GpuProcessD3D11TextureMap.h" 8 9 #include "libyuv.h" 10 #include "mozilla/layers/CompositorThread.h" 11 #include "mozilla/layers/D3D11ZeroCopyTextureImage.h" 12 #include "mozilla/layers/HelpersD3D11.h" 13 #include "mozilla/layers/TextureHostWrapperD3D11.h" 14 #include "mozilla/ProfilerMarkers.h" 15 #include "mozilla/SharedThreadPool.h" 16 #include "mozilla/webrender/RenderThread.h" 17 18 namespace mozilla { 19 20 namespace layers { 21 22 StaticAutoPtr<GpuProcessD3D11TextureMap> GpuProcessD3D11TextureMap::sInstance; 23 24 /* static */ 25 void GpuProcessD3D11TextureMap::Init() { 26 MOZ_ASSERT(XRE_IsGPUProcess()); 27 sInstance = new GpuProcessD3D11TextureMap(); 28 } 29 30 /* static */ 31 void GpuProcessD3D11TextureMap::Shutdown() { 32 MOZ_ASSERT(XRE_IsGPUProcess()); 33 sInstance = nullptr; 34 } 35 36 /* static */ 37 GpuProcessTextureId GpuProcessD3D11TextureMap::GetNextTextureId() { 38 MOZ_ASSERT(XRE_IsGPUProcess()); 39 return GpuProcessTextureId::GetNext(); 40 } 41 42 GpuProcessD3D11TextureMap::GpuProcessD3D11TextureMap() 43 : mMonitor("GpuProcessD3D11TextureMap::mMonitor") {} 44 45 GpuProcessD3D11TextureMap::~GpuProcessD3D11TextureMap() {} 46 47 void GpuProcessD3D11TextureMap::Register( 48 GpuProcessTextureId aTextureId, ID3D11Texture2D* aTexture, 49 uint32_t aArrayIndex, const gfx::IntSize& aSize, 50 RefPtr<ZeroCopyUsageInfo> aUsageInfo, 51 RefPtr<gfx::FileHandleWrapper> aSharedHandle) { 52 MonitorAutoLock lock(mMonitor); 53 Register(lock, aTextureId, aTexture, aArrayIndex, aSize, aUsageInfo, 54 aSharedHandle); 55 } 56 void GpuProcessD3D11TextureMap::Register( 57 const MonitorAutoLock& aProofOfLock, GpuProcessTextureId aTextureId, 58 ID3D11Texture2D* aTexture, uint32_t aArrayIndex, const gfx::IntSize& aSize, 59 RefPtr<ZeroCopyUsageInfo> aUsageInfo, 60 RefPtr<gfx::FileHandleWrapper> aSharedHandle) { 61 MOZ_RELEASE_ASSERT(aTexture); 62 63 auto it = mD3D11TexturesById.find(aTextureId); 64 if (it != mD3D11TexturesById.end()) { 65 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 66 return; 67 } 68 mD3D11TexturesById.emplace( 69 aTextureId, 70 TextureHolder(aTexture, aArrayIndex, aSize, aUsageInfo, aSharedHandle)); 71 } 72 73 void GpuProcessD3D11TextureMap::Unregister(GpuProcessTextureId aTextureId) { 74 MonitorAutoLock lock(mMonitor); 75 76 auto it = mD3D11TexturesById.find(aTextureId); 77 if (it == mD3D11TexturesById.end()) { 78 return; 79 } 80 mD3D11TexturesById.erase(it); 81 } 82 83 RefPtr<ID3D11Texture2D> GpuProcessD3D11TextureMap::GetTexture( 84 GpuProcessTextureId aTextureId) { 85 MonitorAutoLock lock(mMonitor); 86 87 auto it = mD3D11TexturesById.find(aTextureId); 88 if (it == mD3D11TexturesById.end()) { 89 return nullptr; 90 } 91 92 return it->second.mTexture; 93 } 94 95 Maybe<HANDLE> GpuProcessD3D11TextureMap::GetSharedHandle( 96 GpuProcessTextureId aTextureId) { 97 TextureHolder holder; 98 { 99 MonitorAutoLock lock(mMonitor); 100 101 auto it = mD3D11TexturesById.find(aTextureId); 102 if (it == mD3D11TexturesById.end()) { 103 return Nothing(); 104 } 105 106 if (it->second.mSharedHandle) { 107 return Some(it->second.mSharedHandle->GetHandle()); 108 } 109 110 if (it->second.mCopiedTextureSharedHandle) { 111 return Some(it->second.mCopiedTextureSharedHandle->GetHandle()); 112 } 113 114 holder = it->second; 115 } 116 117 RefPtr<ID3D11Device> device; 118 holder.mTexture->GetDevice(getter_AddRefs(device)); 119 if (!device) { 120 return Nothing(); 121 } 122 123 RefPtr<ID3D11DeviceContext> context; 124 device->GetImmediateContext(getter_AddRefs(context)); 125 if (!context) { 126 return Nothing(); 127 } 128 129 CD3D11_TEXTURE2D_DESC newDesc( 130 DXGI_FORMAT_NV12, holder.mSize.width, holder.mSize.height, 1, 1, 131 D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); 132 newDesc.MiscFlags = 133 D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED; 134 135 RefPtr<ID3D11Texture2D> copiedTexture; 136 HRESULT hr = 137 device->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(copiedTexture)); 138 if (FAILED(hr)) { 139 return Nothing(); 140 } 141 142 D3D11_TEXTURE2D_DESC inDesc; 143 holder.mTexture->GetDesc(&inDesc); 144 145 D3D11_TEXTURE2D_DESC outDesc; 146 copiedTexture->GetDesc(&outDesc); 147 148 UINT height = std::min(inDesc.Height, outDesc.Height); 149 UINT width = std::min(inDesc.Width, outDesc.Width); 150 D3D11_BOX srcBox = {0, 0, 0, width, height, 1}; 151 152 context->CopySubresourceRegion(copiedTexture, 0, 0, 0, 0, holder.mTexture, 153 holder.mArrayIndex, &srcBox); 154 155 RefPtr<IDXGIResource1> resource; 156 copiedTexture->QueryInterface((IDXGIResource1**)getter_AddRefs(resource)); 157 if (!resource) { 158 return Nothing(); 159 } 160 161 HANDLE sharedHandle; 162 hr = resource->CreateSharedHandle( 163 nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, 164 &sharedHandle); 165 if (FAILED(hr)) { 166 return Nothing(); 167 } 168 169 RefPtr<gfx::FileHandleWrapper> handle = 170 new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle)); 171 172 RefPtr<ID3D11Query> query; 173 CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT); 174 hr = device->CreateQuery(&desc, getter_AddRefs(query)); 175 if (FAILED(hr) || !query) { 176 gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << gfx::hexa(hr); 177 return Nothing(); 178 } 179 180 context->End(query); 181 182 BOOL result; 183 bool ret = WaitForFrameGPUQuery(device, context, query, &result); 184 if (!ret) { 185 gfxCriticalNoteOnce << "WaitForFrameGPUQuery() failed"; 186 } 187 188 { 189 MonitorAutoLock lock(mMonitor); 190 191 auto it = mD3D11TexturesById.find(aTextureId); 192 if (it == mD3D11TexturesById.end()) { 193 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 194 return Nothing(); 195 } 196 197 // Disable no video copy for future decoded video frames. Since 198 // Get SharedHandle of copied Texture() is slow. 199 if (it->second.mZeroCopyUsageInfo) { 200 it->second.mZeroCopyUsageInfo->DisableZeroCopyNV12Texture(); 201 } 202 203 it->second.mCopiedTexture = copiedTexture; 204 it->second.mCopiedTextureSharedHandle = handle; 205 } 206 207 return Some(handle->GetHandle()); 208 } 209 210 void GpuProcessD3D11TextureMap::DisableZeroCopyNV12Texture( 211 GpuProcessTextureId aTextureId) { 212 MonitorAutoLock lock(mMonitor); 213 214 auto it = mD3D11TexturesById.find(aTextureId); 215 if (it == mD3D11TexturesById.end()) { 216 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 217 return; 218 } 219 220 if (!it->second.mZeroCopyUsageInfo) { 221 return; 222 } 223 224 // Disable no video copy for future decoded video frames. Since 225 // Get SharedHandle of copied Texture() is slow. 226 it->second.mZeroCopyUsageInfo->DisableZeroCopyNV12Texture(); 227 } 228 229 size_t GpuProcessD3D11TextureMap::GetWaitingTextureCount() const { 230 MonitorAutoLock lock(mMonitor); 231 return mWaitingTextures.size(); 232 } 233 234 bool GpuProcessD3D11TextureMap::WaitTextureReady( 235 const GpuProcessTextureId aTextureId) { 236 MOZ_ASSERT(wr::RenderThread::IsInRenderThread()); 237 238 MonitorAutoLock lock(mMonitor); 239 auto it = mWaitingTextures.find(aTextureId); 240 if (it == mWaitingTextures.end()) { 241 return true; 242 } 243 244 auto start = TimeStamp::Now(); 245 const TimeDuration timeout = TimeDuration::FromMilliseconds(1000); 246 while (1) { 247 CVStatus status = mMonitor.Wait(timeout); 248 if (status == CVStatus::Timeout) { 249 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 250 gfxCriticalNoteOnce << "GpuProcessTextureI wait time out id:" 251 << uint64_t(aTextureId); 252 return false; 253 } 254 255 auto it = mWaitingTextures.find(aTextureId); 256 if (it == mWaitingTextures.end()) { 257 break; 258 } 259 } 260 261 auto end = TimeStamp::Now(); 262 const auto durationMs = static_cast<uint32_t>((end - start).ToMilliseconds()); 263 nsPrintfCString marker("TextureReadyWait %ums ", durationMs); 264 PROFILER_MARKER_TEXT("PresentWait", GRAPHICS, {}, marker); 265 266 return true; 267 } 268 269 void GpuProcessD3D11TextureMap::PostUpdateTextureDataTask( 270 const GpuProcessTextureId aTextureId, TextureHost* aTextureHost, 271 TextureHost* aWrappedTextureHost, 272 TextureWrapperD3D11Allocator* aAllocator) { 273 { 274 MonitorAutoLock lock(mMonitor); 275 276 auto it = mWaitingTextures.find(aTextureId); 277 if (it != mWaitingTextures.end()) { 278 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 279 return; 280 } 281 282 auto updatingTexture = MakeUnique<UpdatingTextureHolder>( 283 aTextureId, aTextureHost, aWrappedTextureHost, aAllocator); 284 285 mWaitingTextures.emplace(aTextureId); 286 mWaitingTextureQueue.push_back(std::move(updatingTexture)); 287 } 288 289 RefPtr<Runnable> runnable = NS_NewRunnableFunction( 290 "GpuProcessD3D11TextureMap::PostUpdateTextureDataTaskRunnable", []() { 291 GpuProcessD3D11TextureMap::Get()->HandleInTextureUpdateThread(); 292 }); 293 nsCOMPtr<nsIEventTarget> thread = aAllocator->mThread; 294 thread->Dispatch(runnable.forget()); 295 } 296 297 void GpuProcessD3D11TextureMap::HandleInTextureUpdateThread() { 298 UniquePtr<UpdatingTextureHolder> textureHolder; 299 { 300 MonitorAutoLock lock(mMonitor); 301 302 if (mWaitingTextureQueue.empty()) { 303 return; 304 } 305 textureHolder = std::move(mWaitingTextureQueue.front()); 306 mWaitingTextureQueue.pop_front(); 307 } 308 309 RefPtr<ID3D11Texture2D> texture = UpdateTextureData(textureHolder.get()); 310 311 { 312 MonitorAutoLock lock(mMonitor); 313 if (texture) { 314 auto size = textureHolder->mWrappedTextureHost->GetSize(); 315 Register(lock, textureHolder->mTextureId, texture, /* aArrayIndex */ 0, 316 size, /* aUsageInfo */ nullptr, /* aSharedHandle */ nullptr); 317 } 318 mWaitingTextures.erase(textureHolder->mTextureId); 319 MOZ_ASSERT(mWaitingTextures.size() == mWaitingTextureQueue.size()); 320 mMonitor.Notify(); 321 } 322 323 // Release UpdatingTextureHolder in CompositorThread 324 RefPtr<Runnable> runnable = NS_NewRunnableFunction( 325 "GpuProcessD3D11TextureMap::HandleInTextureUpdateThread::Runnable", 326 [textureHolder = std::move(textureHolder)]() mutable { 327 textureHolder = nullptr; 328 }); 329 CompositorThread()->Dispatch(runnable.forget()); 330 } 331 332 RefPtr<ID3D11Texture2D> GpuProcessD3D11TextureMap::UpdateTextureData( 333 UpdatingTextureHolder* aHolder) { 334 MOZ_ASSERT(aHolder); 335 MOZ_ASSERT(aHolder->mAllocator->mThread->IsOnCurrentThread()); 336 337 auto* bufferTexture = aHolder->mWrappedTextureHost->AsBufferTextureHost(); 338 MOZ_ASSERT(bufferTexture); 339 if (!bufferTexture) { 340 MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 341 return nullptr; 342 } 343 344 auto size = bufferTexture->GetSize(); 345 346 RefPtr<ID3D11Texture2D> texture2D = 347 aHolder->mAllocator->CreateOrRecycle(gfx::SurfaceFormat::NV12, size); 348 if (!texture2D) { 349 return nullptr; 350 } 351 352 RefPtr<ID3D11Texture2D> stagingTexture = 353 aHolder->mAllocator->GetStagingTextureNV12(); 354 if (!stagingTexture) { 355 return nullptr; 356 } 357 358 RefPtr<ID3D11DeviceContext> context; 359 RefPtr<ID3D11Device> device = aHolder->mAllocator->GetDevice(); 360 device->GetImmediateContext(getter_AddRefs(context)); 361 if (!context) { 362 return nullptr; 363 } 364 365 RefPtr<ID3D10Multithread> mt; 366 HRESULT hr = device->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt)); 367 if (FAILED(hr) || !mt) { 368 gfxCriticalError() << "Multithread safety interface not supported. " << hr; 369 return nullptr; 370 } 371 372 if (!mt->GetMultithreadProtected()) { 373 gfxCriticalError() << "Device used not marked as multithread-safe."; 374 return nullptr; 375 } 376 377 D3D11MTAutoEnter mtAutoEnter(mt.forget()); 378 379 AutoLockD3D11Texture lockSt(stagingTexture); 380 381 D3D11_MAP mapType = D3D11_MAP_WRITE; 382 D3D11_MAPPED_SUBRESOURCE mappedResource; 383 384 hr = context->Map(stagingTexture, 0, mapType, 0, &mappedResource); 385 if (FAILED(hr)) { 386 gfxCriticalNoteOnce << "Mapping D3D11 staging texture failed: " 387 << gfx::hexa(hr); 388 return nullptr; 389 } 390 391 const size_t destStride = mappedResource.RowPitch; 392 uint8_t* yDestPlaneStart = reinterpret_cast<uint8_t*>(mappedResource.pData); 393 uint8_t* uvDestPlaneStart = reinterpret_cast<uint8_t*>(mappedResource.pData) + 394 destStride * size.height; 395 // Convert I420 to NV12, 396 const uint8_t* yChannel = bufferTexture->GetYChannel(); 397 const uint8_t* cbChannel = bufferTexture->GetCbChannel(); 398 const uint8_t* crChannel = bufferTexture->GetCrChannel(); 399 int32_t yStride = bufferTexture->GetYStride(); 400 int32_t cbCrStride = bufferTexture->GetCbCrStride(); 401 402 libyuv::I420ToNV12(yChannel, yStride, cbChannel, cbCrStride, crChannel, 403 cbCrStride, yDestPlaneStart, destStride, uvDestPlaneStart, 404 destStride, size.width, size.height); 405 406 context->Unmap(stagingTexture, 0); 407 408 context->CopyResource(texture2D, stagingTexture); 409 410 return texture2D; 411 } 412 413 GpuProcessD3D11TextureMap::TextureHolder::TextureHolder( 414 ID3D11Texture2D* aTexture, uint32_t aArrayIndex, const gfx::IntSize& aSize, 415 RefPtr<ZeroCopyUsageInfo> aUsageInfo, 416 RefPtr<gfx::FileHandleWrapper> aSharedHandle) 417 : mTexture(aTexture), 418 mArrayIndex(aArrayIndex), 419 mSize(aSize), 420 mZeroCopyUsageInfo(aUsageInfo), 421 mSharedHandle(aSharedHandle) {} 422 423 GpuProcessD3D11TextureMap::UpdatingTextureHolder::UpdatingTextureHolder( 424 const GpuProcessTextureId aTextureId, TextureHost* aTextureHost, 425 TextureHost* aWrappedTextureHost, TextureWrapperD3D11Allocator* aAllocator) 426 : mTextureId(aTextureId), 427 mTextureHost(aTextureHost), 428 mWrappedTextureHost(aWrappedTextureHost), 429 mAllocator(aAllocator) { 430 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 431 } 432 433 GpuProcessD3D11TextureMap::UpdatingTextureHolder::~UpdatingTextureHolder() { 434 MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); 435 } 436 437 } // namespace layers 438 } // namespace mozilla