blob: c5b35614b0dfb16c77293521c3021dcdf0b34220 [file] [log] [blame]
// Copyright 2022 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dawn/native/d3d/ExternalImageDXGIImpl.h"
#include <utility>
#include <vector>
#include "dawn/common/Log.h"
#include "dawn/native/D3D12Backend.h"
#include "dawn/native/DawnNative.h"
#include "dawn/native/d3d/DeviceD3D.h"
#include "dawn/native/d3d/Fence.h"
#include "dawn/native/d3d/Forward.h"
#include "dawn/native/d3d/TextureD3D.h"
namespace dawn::native::d3d {
MaybeError ValidateTextureDescriptorCanBeWrapped(const TextureDescriptor* descriptor) {
DAWN_INVALID_IF(descriptor->dimension != wgpu::TextureDimension::e2D,
"Texture dimension (%s) is not %s.", descriptor->dimension,
wgpu::TextureDimension::e2D);
DAWN_INVALID_IF(descriptor->mipLevelCount != 1, "Mip level count (%u) is not 1.",
descriptor->mipLevelCount);
DAWN_INVALID_IF(descriptor->size.depthOrArrayLayers != 1, "Array layer count (%u) is not 1.",
descriptor->size.depthOrArrayLayers);
DAWN_INVALID_IF(descriptor->sampleCount != 1, "Sample count (%u) is not 1.",
descriptor->sampleCount);
return {};
}
ExternalImageDXGIImpl::ExternalImageDXGIImpl(Device* backendDevice,
ComPtr<IUnknown> d3dResource,
const TextureDescriptor* textureDescriptor)
: mBackendDevice(backendDevice),
mD3DResource(std::move(d3dResource)),
mUsage(textureDescriptor->usage),
mDimension(textureDescriptor->dimension),
mSize(textureDescriptor->size),
mFormat(textureDescriptor->format),
mMipLevelCount(textureDescriptor->mipLevelCount),
mSampleCount(textureDescriptor->sampleCount),
mViewFormats(textureDescriptor->viewFormats,
textureDescriptor->viewFormats + textureDescriptor->viewFormatCount) {
DAWN_ASSERT(mBackendDevice->IsLockedByCurrentThreadIfNeeded());
DAWN_ASSERT(mBackendDevice != nullptr);
DAWN_ASSERT(!textureDescriptor->nextInChain ||
textureDescriptor->nextInChain->sType ==
wgpu::SType::DawnTextureInternalUsageDescriptor);
if (textureDescriptor->nextInChain) {
mUsageInternal = reinterpret_cast<const wgpu::DawnTextureInternalUsageDescriptor*>(
textureDescriptor->nextInChain)
->internalUsage;
}
// If the resource has IDXGIKeyedMutex interface, it will be used for synchronization.
// TODO(dawn:1906): remove the mDXGIKeyedMutex when it is not used in chrome.
mD3DResource.As(&mDXGIKeyedMutex);
}
ExternalImageDXGIImpl::~ExternalImageDXGIImpl() {
DAWN_ASSERT(mBackendDevice->IsLockedByCurrentThreadIfNeeded());
mDXGIKeyedMutexReleaser.reset();
DestroyInternal();
}
Mutex::AutoLock ExternalImageDXGIImpl::GetScopedDeviceLock() const {
return mBackendDevice->GetScopedLock();
}
bool ExternalImageDXGIImpl::IsValid() const {
DAWN_ASSERT(mBackendDevice->IsLockedByCurrentThreadIfNeeded());
return IsInList();
}
void ExternalImageDXGIImpl::DestroyInternal() {
DAWN_ASSERT(mBackendDevice->IsLockedByCurrentThreadIfNeeded());
if (IsInList()) {
mD3DResource = nullptr;
}
if (IsInList()) {
RemoveFromList();
}
}
WGPUTexture ExternalImageDXGIImpl::BeginAccess(
const d3d::ExternalImageDXGIBeginAccessDescriptor* descriptor) {
DAWN_ASSERT(mBackendDevice->IsLockedByCurrentThreadIfNeeded());
DAWN_ASSERT(descriptor != nullptr);
if (!IsInList()) {
dawn::ErrorLog() << "Cannot use external image after device destruction";
return nullptr;
}
// Ensure the texture usage is allowed
if (!IsSubset(descriptor->usage, static_cast<WGPUTextureUsageFlags>(mUsage))) {
dawn::ErrorLog() << "Texture usage is not valid for external image";
return nullptr;
}
DAWN_ASSERT(mBackendDevice != nullptr);
if (mBackendDevice->GetValidInternalFormat(mFormat).IsMultiPlanar() &&
!descriptor->isInitialized) {
bool consumed = mBackendDevice->ConsumedError(DAWN_VALIDATION_ERROR(
"External textures with multiplanar formats must be initialized."));
DAWN_UNUSED(consumed);
return nullptr;
}
TextureDescriptor textureDescriptor = {};
textureDescriptor.usage = static_cast<wgpu::TextureUsage>(descriptor->usage);
textureDescriptor.dimension = mDimension;
textureDescriptor.size = {mSize.width, mSize.height, mSize.depthOrArrayLayers};
textureDescriptor.format = mFormat;
textureDescriptor.mipLevelCount = mMipLevelCount;
textureDescriptor.sampleCount = mSampleCount;
textureDescriptor.viewFormats = mViewFormats.data();
textureDescriptor.viewFormatCount = mViewFormats.size();
DawnTextureInternalUsageDescriptor internalDesc = {};
if (mUsageInternal != wgpu::TextureUsage::None) {
textureDescriptor.nextInChain = &internalDesc;
internalDesc.internalUsage = mUsageInternal;
}
std::vector<Ref<Fence>> waitFences;
for (const d3d::ExternalImageDXGIFenceDescriptor& fenceDescriptor : descriptor->waitFences) {
Ref<Fence> fence;
if (mBackendDevice->ConsumedError(
ToBackend(mBackendDevice.Get())->CreateFence(&fenceDescriptor), &fence)) {
dawn::ErrorLog() << "Unable to create D3D11 fence for external image";
return nullptr;
}
waitFences.push_back(std::move(fence));
}
Ref<TextureBase> texture =
ToBackend(mBackendDevice.Get())
->CreateD3DExternalTexture(&textureDescriptor, mD3DResource, std::move(waitFences),
descriptor->isSwapChainTexture, descriptor->isInitialized);
if (mDXGIKeyedMutex && mAccessCount == 0) {
HRESULT hr = mDXGIKeyedMutex->AcquireSync(kDXGIKeyedMutexAcquireKey, INFINITE);
if (FAILED(hr)) {
dawn::ErrorLog() << "Failed to acquire keyed mutex for external image";
return nullptr;
}
mDXGIKeyedMutexReleaser.emplace(mDXGIKeyedMutex);
}
++mAccessCount;
return ToAPI(texture.Detach());
}
void ExternalImageDXGIImpl::EndAccess(WGPUTexture texture,
d3d::ExternalImageDXGIFenceDescriptor* signalFence) {
DAWN_ASSERT(mBackendDevice->IsLockedByCurrentThreadIfNeeded());
if (!IsInList()) {
dawn::ErrorLog() << "Cannot use external image after device destruction";
return;
}
DAWN_ASSERT(mBackendDevice != nullptr);
DAWN_ASSERT(signalFence != nullptr);
Texture* backendTexture = ToBackend(FromAPI(texture));
DAWN_ASSERT(backendTexture != nullptr);
ExecutionSerial fenceValue;
if (mBackendDevice->ConsumedError(backendTexture->EndAccess(), &fenceValue)) {
dawn::ErrorLog() << "D3D11 fence end access failed";
return;
}
signalFence->fenceHandle = ToBackend(mBackendDevice.Get())->GetFenceHandle();
signalFence->fenceValue = static_cast<uint64_t>(fenceValue);
--mAccessCount;
if (mDXGIKeyedMutexReleaser && mAccessCount == 0) {
mDXGIKeyedMutexReleaser.reset();
}
}
} // namespace dawn::native::d3d