summaryrefslogtreecommitdiffstats
path: root/gfx/layers/d3d11/GpuProcessD3D11TextureMap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/d3d11/GpuProcessD3D11TextureMap.cpp')
-rw-r--r--gfx/layers/d3d11/GpuProcessD3D11TextureMap.cpp409
1 files changed, 409 insertions, 0 deletions
diff --git a/gfx/layers/d3d11/GpuProcessD3D11TextureMap.cpp b/gfx/layers/d3d11/GpuProcessD3D11TextureMap.cpp
new file mode 100644
index 0000000000..36651b3279
--- /dev/null
+++ b/gfx/layers/d3d11/GpuProcessD3D11TextureMap.cpp
@@ -0,0 +1,409 @@
+/* -*- 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> 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<IMFSampleUsageInfo> 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<IMFSampleUsageInfo> 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<ID3D11Texture2D> GpuProcessD3D11TextureMap::GetTexture(
+ GpuProcessTextureId aTextureId) {
+ MonitorAutoLock lock(mMonitor);
+
+ auto it = mD3D11TexturesById.find(aTextureId);
+ if (it == mD3D11TexturesById.end()) {
+ return nullptr;
+ }
+
+ return it->second.mTexture;
+}
+
+Maybe<HANDLE> 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<ID3D11Device> device;
+ holder.mTexture->GetDevice(getter_AddRefs(device));
+ if (!device) {
+ return Nothing();
+ }
+
+ RefPtr<ID3D11DeviceContext> 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<ID3D11Texture2D> 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<IDXGIResource1> 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<gfx::FileHandleWrapper> handle =
+ new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle));
+
+ RefPtr<ID3D11Query> 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<uint32_t>((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<UpdatingTextureHolder>(
+ aTextureId, aTextureHost, aWrappedTextureHost, aAllocator);
+
+ mWaitingTextures.emplace(aTextureId);
+ mWaitingTextureQueue.push_back(std::move(updatingTexture));
+ }
+
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+ "GpuProcessD3D11TextureMap::PostUpdateTextureDataTaskRunnable", []() {
+ GpuProcessD3D11TextureMap::Get()->HandleInTextureUpdateThread();
+ });
+ nsCOMPtr<nsIEventTarget> thread = aAllocator->mThread;
+ thread->Dispatch(runnable.forget());
+}
+
+void GpuProcessD3D11TextureMap::HandleInTextureUpdateThread() {
+ UniquePtr<UpdatingTextureHolder> textureHolder;
+ {
+ MonitorAutoLock lock(mMonitor);
+
+ if (mWaitingTextureQueue.empty()) {
+ return;
+ }
+ textureHolder = std::move(mWaitingTextureQueue.front());
+ mWaitingTextureQueue.pop_front();
+ }
+
+ RefPtr<ID3D11Texture2D> 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> runnable = NS_NewRunnableFunction(
+ "GpuProcessD3D11TextureMap::HandleInTextureUpdateThread::Runnable",
+ [textureHolder = std::move(textureHolder)]() mutable {
+ textureHolder = nullptr;
+ });
+ CompositorThread()->Dispatch(runnable.forget());
+}
+
+RefPtr<ID3D11Texture2D> 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<ID3D11Texture2D> texture2D =
+ aHolder->mAllocator->CreateOrRecycle(gfx::SurfaceFormat::NV12, size);
+ if (!texture2D) {
+ return nullptr;
+ }
+
+ RefPtr<ID3D11Texture2D> stagingTexture =
+ aHolder->mAllocator->GetStagingTextureNV12();
+ if (!stagingTexture) {
+ return nullptr;
+ }
+
+ RefPtr<ID3D11DeviceContext> context;
+ RefPtr<ID3D11Device> device = aHolder->mAllocator->GetDevice();
+ device->GetImmediateContext(getter_AddRefs(context));
+ if (!context) {
+ return nullptr;
+ }
+
+ RefPtr<ID3D10Multithread> 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<uint8_t*>(mappedResource.pData);
+ uint8_t* uvDestPlaneStart = reinterpret_cast<uint8_t*>(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<IMFSampleUsageInfo> 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