/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 "ExternalTextureD3D11.h" #include #include "mozilla/gfx/DeviceManagerDx.h" #include "mozilla/gfx/Logging.h" #include "mozilla/layers/FenceD3D11.h" #include "mozilla/layers/CompositeProcessD3D11FencesHolderMap.h" #include "mozilla/layers/ImageDataSerializer.h" #include "mozilla/webgpu/WebGPUParent.h" namespace mozilla::webgpu { // static UniquePtr ExternalTextureD3D11::Create( WebGPUParent* aParent, const ffi::WGPUDeviceId aDeviceId, const uint32_t aWidth, const uint32_t aHeight, const struct ffi::WGPUTextureFormat aFormat, const ffi::WGPUTextureUsages aUsage) { auto* fencesHolderMap = layers::CompositeProcessD3D11FencesHolderMap::Get(); if (!fencesHolderMap) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); gfxCriticalNoteOnce << "Failed to get FencesHolderMap"; return nullptr; } RefPtr fenceHandle = aParent->GetDeviceFenceHandle(aDeviceId); if (!fenceHandle) { gfxCriticalNoteOnce << "Failed to get fenceHandle"; return nullptr; } RefPtr fence = layers::FenceD3D11::CreateFromHandle(fenceHandle, /* aDevice */ nullptr); if (!fence) { gfxCriticalNoteOnce << "Failed create FenceD3D11"; return nullptr; } const RefPtr d3d11Device = gfx::DeviceManagerDx::Get()->GetCompositorDevice(); if (!d3d11Device) { gfxCriticalNoteOnce << "CompositorDevice does not exist"; return nullptr; } if (aFormat.tag != ffi::WGPUTextureFormat_Bgra8Unorm) { gfxCriticalNoteOnce << "Non supported format: " << aFormat.tag; return nullptr; } CD3D11_TEXTURE2D_DESC desc( DXGI_FORMAT_B8G8R8A8_UNORM, aWidth, aHeight, 1, 1, D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET); if (aUsage & WGPUTextureUsages_STORAGE_BINDING) { desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; } desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED; RefPtr texture; HRESULT hr = d3d11Device->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture)); if (FAILED(hr)) { gfxCriticalNoteOnce << "CreateTexture2D failed: " << gfx::hexa(hr); return nullptr; } RefPtr resource; texture->QueryInterface((IDXGIResource1**)getter_AddRefs(resource)); if (!resource) { gfxCriticalNoteOnce << "Failed to get IDXGIResource"; return nullptr; } HANDLE sharedHandle; hr = resource->CreateSharedHandle( nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, &sharedHandle); if (FAILED(hr) || !sharedHandle) { gfxCriticalNoteOnce << "GetSharedHandle failed: " << gfx::hexa(hr); return nullptr; } RefPtr handle = new gfx::FileHandleWrapper(UniqueFileHandle(sharedHandle)); auto fencesHolderId = layers::CompositeProcessFencesHolderId::GetNext(); fencesHolderMap->Register(fencesHolderId); return MakeUnique(aWidth, aHeight, aFormat, aUsage, texture, std::move(handle), fencesHolderId, std::move(fence)); } ExternalTextureD3D11::ExternalTextureD3D11( const uint32_t aWidth, const uint32_t aHeight, const struct ffi::WGPUTextureFormat aFormat, const ffi::WGPUTextureUsages aUsage, const RefPtr aTexture, RefPtr&& aSharedHandle, const layers::CompositeProcessFencesHolderId aFencesHolderId, RefPtr&& aWriteFence) : ExternalTexture(aWidth, aHeight, aFormat, aUsage), mTexture(aTexture), mSharedHandle(std::move(aSharedHandle)), mFencesHolderId(aFencesHolderId), mWriteFence(std::move(aWriteFence)) { MOZ_ASSERT(mTexture); } ExternalTextureD3D11::~ExternalTextureD3D11() { auto* fencesHolderMap = layers::CompositeProcessD3D11FencesHolderMap::Get(); if (fencesHolderMap) { fencesHolderMap->Unregister(mFencesHolderId); } else { gfxCriticalNoteOnce << "CompositeProcessD3D11FencesHolderMap does not exist"; } } void* ExternalTextureD3D11::GetExternalTextureHandle() { RefPtr device; mTexture->GetDevice(getter_AddRefs(device)); auto* fencesHolderMap = layers::CompositeProcessD3D11FencesHolderMap::Get(); MOZ_ASSERT(fencesHolderMap); // XXX deliver fences to wgpu fencesHolderMap->WaitAllFencesAndForget(mFencesHolderId, device); return mSharedHandle->GetHandle(); } Maybe ExternalTextureD3D11::ToSurfaceDescriptor() { MOZ_ASSERT(mSubmissionIndex > 0); mWriteFence->Update(mSubmissionIndex); auto* fencesHolderMap = layers::CompositeProcessD3D11FencesHolderMap::Get(); MOZ_ASSERT(fencesHolderMap); fencesHolderMap->SetWriteFence(mFencesHolderId, mWriteFence); const auto format = gfx::SurfaceFormat::B8G8R8A8; return Some(layers::SurfaceDescriptorD3D10( mSharedHandle, /* gpuProcessTextureId */ Nothing(), /* arrayIndex */ 0, format, gfx::IntSize(mWidth, mHeight), gfx::ColorSpace2::SRGB, gfx::ColorRange::FULL, /* hasKeyedMutex */ false, Some(mFencesHolderId))); } void ExternalTextureD3D11::GetSnapshot(const ipc::Shmem& aDestShmem, const gfx::IntSize& aSize) { RefPtr device; mTexture->GetDevice(getter_AddRefs(device)); if (!device) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); gfxCriticalNoteOnce << "Failed to get ID3D11Device"; return; } RefPtr deviceContext; device->GetImmediateContext(getter_AddRefs(deviceContext)); if (!deviceContext) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); gfxCriticalNoteOnce << "Failed to get ID3D11DeviceContext"; return; } D3D11_TEXTURE2D_DESC textureDesc = {0}; mTexture->GetDesc(&textureDesc); textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; textureDesc.Usage = D3D11_USAGE_STAGING; textureDesc.BindFlags = 0; textureDesc.MiscFlags = 0; textureDesc.MipLevels = 1; RefPtr cpuTexture; HRESULT hr = device->CreateTexture2D(&textureDesc, nullptr, getter_AddRefs(cpuTexture)); if (FAILED(hr)) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); gfxCriticalNote << "Failed to create ID3D11Texture2D: " << gfx::hexa(hr); return; } deviceContext->CopyResource(cpuTexture, mTexture); D3D11_MAPPED_SUBRESOURCE map; hr = deviceContext->Map(cpuTexture, 0, D3D11_MAP_READ, 0, &map); if (FAILED(hr)) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); gfxCriticalNote << "Failed to map ID3D11Texture2D: " << gfx::hexa(hr); return; } const uint32_t stride = layers::ImageDataSerializer::ComputeRGBStride( gfx::SurfaceFormat::B8G8R8A8, aSize.width); uint8_t* src = static_cast(map.pData); uint8_t* dst = aDestShmem.get(); MOZ_ASSERT(stride * aSize.height <= aDestShmem.Size()); MOZ_ASSERT(map.RowPitch >= stride); for (int y = 0; y < aSize.height; y++) { memcpy(dst, src, stride); src += map.RowPitch; dst += stride; } deviceContext->Unmap(cpuTexture, 0); } } // namespace mozilla::webgpu