/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GpuProcessD3D11TextureMap.h" #include "libyuv.h" #include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/D3D11TextureIMFSampleImage.h" #include "mozilla/layers/HelpersD3D11.h" #include "mozilla/layers/TextureHostWrapperD3D11.h" #include "mozilla/ProfilerMarkers.h" #include "mozilla/SharedThreadPool.h" #include "mozilla/webrender/RenderThread.h" namespace mozilla { namespace layers { StaticAutoPtr GpuProcessD3D11TextureMap::sInstance; /* static */ void GpuProcessD3D11TextureMap::Init() { MOZ_ASSERT(XRE_IsGPUProcess()); sInstance = new GpuProcessD3D11TextureMap(); } /* static */ void GpuProcessD3D11TextureMap::Shutdown() { MOZ_ASSERT(XRE_IsGPUProcess()); sInstance = nullptr; } /* static */ GpuProcessTextureId GpuProcessD3D11TextureMap::GetNextTextureId() { MOZ_ASSERT(XRE_IsGPUProcess()); return GpuProcessTextureId::GetNext(); } GpuProcessD3D11TextureMap::GpuProcessD3D11TextureMap() : mMonitor("GpuProcessD3D11TextureMap::mMonitor") {} GpuProcessD3D11TextureMap::~GpuProcessD3D11TextureMap() {} void GpuProcessD3D11TextureMap::Register( GpuProcessTextureId aTextureId, ID3D11Texture2D* aTexture, uint32_t aArrayIndex, const gfx::IntSize& aSize, RefPtr aUsageInfo) { MonitorAutoLock lock(mMonitor); Register(lock, aTextureId, aTexture, aArrayIndex, aSize, aUsageInfo); } void GpuProcessD3D11TextureMap::Register( const MonitorAutoLock& aProofOfLock, GpuProcessTextureId aTextureId, ID3D11Texture2D* aTexture, uint32_t aArrayIndex, const gfx::IntSize& aSize, RefPtr aUsageInfo) { MOZ_RELEASE_ASSERT(aTexture); auto it = mD3D11TexturesById.find(aTextureId); if (it != mD3D11TexturesById.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } mD3D11TexturesById.emplace( aTextureId, TextureHolder(aTexture, aArrayIndex, aSize, aUsageInfo)); } void GpuProcessD3D11TextureMap::Unregister(GpuProcessTextureId aTextureId) { MonitorAutoLock lock(mMonitor); auto it = mD3D11TexturesById.find(aTextureId); if (it == mD3D11TexturesById.end()) { return; } mD3D11TexturesById.erase(it); } RefPtr GpuProcessD3D11TextureMap::GetTexture( GpuProcessTextureId aTextureId) { MonitorAutoLock lock(mMonitor); auto it = mD3D11TexturesById.find(aTextureId); if (it == mD3D11TexturesById.end()) { return nullptr; } return it->second.mTexture; } Maybe GpuProcessD3D11TextureMap::GetSharedHandleOfCopiedTexture( GpuProcessTextureId aTextureId) { TextureHolder holder; { MonitorAutoLock lock(mMonitor); auto it = mD3D11TexturesById.find(aTextureId); if (it == mD3D11TexturesById.end()) { return Nothing(); } if (it->second.mCopiedTextureSharedHandle) { return Some(it->second.mCopiedTextureSharedHandle->GetHandle()); } holder = it->second; } RefPtr device; holder.mTexture->GetDevice(getter_AddRefs(device)); if (!device) { return Nothing(); } RefPtr context; device->GetImmediateContext(getter_AddRefs(context)); if (!context) { return Nothing(); } CD3D11_TEXTURE2D_DESC newDesc( DXGI_FORMAT_NV12, holder.mSize.width, holder.mSize.height, 1, 1, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED; RefPtr copiedTexture; HRESULT hr = device->CreateTexture2D(&newDesc, nullptr, getter_AddRefs(copiedTexture)); if (FAILED(hr)) { return Nothing(); } D3D11_TEXTURE2D_DESC inDesc; holder.mTexture->GetDesc(&inDesc); D3D11_TEXTURE2D_DESC outDesc; copiedTexture->GetDesc(&outDesc); UINT height = std::min(inDesc.Height, outDesc.Height); UINT width = std::min(inDesc.Width, outDesc.Width); D3D11_BOX srcBox = {0, 0, 0, width, height, 1}; context->CopySubresourceRegion(copiedTexture, 0, 0, 0, 0, holder.mTexture, holder.mArrayIndex, &srcBox); RefPtr resource; copiedTexture->QueryInterface((IDXGIResource1**)getter_AddRefs(resource)); if (!resource) { return Nothing(); } HANDLE sharedHandle; hr = resource->CreateSharedHandle( nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, &sharedHandle); if (FAILED(hr)) { return Nothing(); } RefPtr handle = new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle)); RefPtr query; CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT); hr = device->CreateQuery(&desc, getter_AddRefs(query)); if (FAILED(hr) || !query) { gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << gfx::hexa(hr); return Nothing(); } context->End(query); BOOL result; bool ret = WaitForFrameGPUQuery(device, context, query, &result); if (!ret) { gfxCriticalNoteOnce << "WaitForFrameGPUQuery() failed"; } { MonitorAutoLock lock(mMonitor); auto it = mD3D11TexturesById.find(aTextureId); if (it == mD3D11TexturesById.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return Nothing(); } // Disable no video copy for future decoded video frames. Since // GetSharedHandleOfCopiedTexture() is slow. if (it->second.mIMFSampleUsageInfo) { it->second.mIMFSampleUsageInfo->DisableZeroCopyNV12Texture(); } it->second.mCopiedTexture = copiedTexture; it->second.mCopiedTextureSharedHandle = handle; } return Some(handle->GetHandle()); } size_t GpuProcessD3D11TextureMap::GetWaitingTextureCount() const { MonitorAutoLock lock(mMonitor); return mWaitingTextures.size(); } bool GpuProcessD3D11TextureMap::WaitTextureReady( const GpuProcessTextureId aTextureId) { MOZ_ASSERT(wr::RenderThread::IsInRenderThread()); MonitorAutoLock lock(mMonitor); auto it = mWaitingTextures.find(aTextureId); if (it == mWaitingTextures.end()) { return true; } auto start = TimeStamp::Now(); const TimeDuration timeout = TimeDuration::FromMilliseconds(1000); while (1) { CVStatus status = mMonitor.Wait(timeout); if (status == CVStatus::Timeout) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); gfxCriticalNoteOnce << "GpuProcessTextureI wait time out id:" << uint64_t(aTextureId); return false; } auto it = mWaitingTextures.find(aTextureId); if (it == mWaitingTextures.end()) { break; } } auto end = TimeStamp::Now(); const auto durationMs = static_cast((end - start).ToMilliseconds()); nsPrintfCString marker("TextureReadyWait %ums ", durationMs); PROFILER_MARKER_TEXT("PresentWait", GRAPHICS, {}, marker); return true; } void GpuProcessD3D11TextureMap::PostUpdateTextureDataTask( const GpuProcessTextureId aTextureId, TextureHost* aTextureHost, TextureHost* aWrappedTextureHost, TextureWrapperD3D11Allocator* aAllocator) { { MonitorAutoLock lock(mMonitor); auto it = mWaitingTextures.find(aTextureId); if (it != mWaitingTextures.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } auto updatingTexture = MakeUnique( aTextureId, aTextureHost, aWrappedTextureHost, aAllocator); mWaitingTextures.emplace(aTextureId); mWaitingTextureQueue.push_back(std::move(updatingTexture)); } RefPtr runnable = NS_NewRunnableFunction( "GpuProcessD3D11TextureMap::PostUpdateTextureDataTaskRunnable", []() { GpuProcessD3D11TextureMap::Get()->HandleInTextureUpdateThread(); }); nsCOMPtr thread = aAllocator->mThread; thread->Dispatch(runnable.forget()); } void GpuProcessD3D11TextureMap::HandleInTextureUpdateThread() { UniquePtr textureHolder; { MonitorAutoLock lock(mMonitor); if (mWaitingTextureQueue.empty()) { return; } textureHolder = std::move(mWaitingTextureQueue.front()); mWaitingTextureQueue.pop_front(); } RefPtr texture = UpdateTextureData(textureHolder.get()); { MonitorAutoLock lock(mMonitor); if (texture) { auto size = textureHolder->mWrappedTextureHost->GetSize(); Register(lock, textureHolder->mTextureId, texture, /* aArrayIndex */ 0, size, /* aUsageInfo */ nullptr); } mWaitingTextures.erase(textureHolder->mTextureId); MOZ_ASSERT(mWaitingTextures.size() == mWaitingTextureQueue.size()); mMonitor.Notify(); } // Release UpdatingTextureHolder in CompositorThread RefPtr runnable = NS_NewRunnableFunction( "GpuProcessD3D11TextureMap::HandleInTextureUpdateThread::Runnable", [textureHolder = std::move(textureHolder)]() mutable { textureHolder = nullptr; }); CompositorThread()->Dispatch(runnable.forget()); } RefPtr GpuProcessD3D11TextureMap::UpdateTextureData( UpdatingTextureHolder* aHolder) { MOZ_ASSERT(aHolder); MOZ_ASSERT(aHolder->mAllocator->mThread->IsOnCurrentThread()); auto* bufferTexture = aHolder->mWrappedTextureHost->AsBufferTextureHost(); MOZ_ASSERT(bufferTexture); if (!bufferTexture) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return nullptr; } auto size = bufferTexture->GetSize(); RefPtr texture2D = aHolder->mAllocator->CreateOrRecycle(gfx::SurfaceFormat::NV12, size); if (!texture2D) { return nullptr; } RefPtr stagingTexture = aHolder->mAllocator->GetStagingTextureNV12(); if (!stagingTexture) { return nullptr; } RefPtr context; RefPtr device = aHolder->mAllocator->GetDevice(); device->GetImmediateContext(getter_AddRefs(context)); if (!context) { return nullptr; } RefPtr mt; HRESULT hr = device->QueryInterface((ID3D10Multithread**)getter_AddRefs(mt)); if (FAILED(hr) || !mt) { gfxCriticalError() << "Multithread safety interface not supported. " << hr; return nullptr; } if (!mt->GetMultithreadProtected()) { gfxCriticalError() << "Device used not marked as multithread-safe."; return nullptr; } D3D11MTAutoEnter mtAutoEnter(mt.forget()); AutoLockD3D11Texture lockSt(stagingTexture); D3D11_MAP mapType = D3D11_MAP_WRITE; D3D11_MAPPED_SUBRESOURCE mappedResource; hr = context->Map(stagingTexture, 0, mapType, 0, &mappedResource); if (FAILED(hr)) { gfxCriticalNoteOnce << "Mapping D3D11 staging texture failed: " << gfx::hexa(hr); return nullptr; } const size_t destStride = mappedResource.RowPitch; uint8_t* yDestPlaneStart = reinterpret_cast(mappedResource.pData); uint8_t* uvDestPlaneStart = reinterpret_cast(mappedResource.pData) + destStride * size.height; // Convert I420 to NV12, const uint8_t* yChannel = bufferTexture->GetYChannel(); const uint8_t* cbChannel = bufferTexture->GetCbChannel(); const uint8_t* crChannel = bufferTexture->GetCrChannel(); int32_t yStride = bufferTexture->GetYStride(); int32_t cbCrStride = bufferTexture->GetCbCrStride(); libyuv::I420ToNV12(yChannel, yStride, cbChannel, cbCrStride, crChannel, cbCrStride, yDestPlaneStart, destStride, uvDestPlaneStart, destStride, size.width, size.height); context->Unmap(stagingTexture, 0); context->CopyResource(texture2D, stagingTexture); return texture2D; } GpuProcessD3D11TextureMap::TextureHolder::TextureHolder( ID3D11Texture2D* aTexture, uint32_t aArrayIndex, const gfx::IntSize& aSize, RefPtr aUsageInfo) : mTexture(aTexture), mArrayIndex(aArrayIndex), mSize(aSize), mIMFSampleUsageInfo(aUsageInfo) {} GpuProcessD3D11TextureMap::UpdatingTextureHolder::UpdatingTextureHolder( const GpuProcessTextureId aTextureId, TextureHost* aTextureHost, TextureHost* aWrappedTextureHost, TextureWrapperD3D11Allocator* aAllocator) : mTextureId(aTextureId), mTextureHost(aTextureHost), mWrappedTextureHost(aWrappedTextureHost), mAllocator(aAllocator) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); } GpuProcessD3D11TextureMap::UpdatingTextureHolder::~UpdatingTextureHolder() { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); } } // namespace layers } // namespace mozilla