blob: 0a538c92c7272a847cd1f181ede0b92e2ee9e2eb [file] [log] [blame]
// Copyright 2023 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "dawn/native/d3d11/DeviceD3D11.h"
#include <algorithm>
#include <limits>
#include <sstream>
#include <utility>
#include "dawn/common/GPUInfo.h"
#include "dawn/native/ChainUtils.h"
#include "dawn/native/D3D11Backend.h"
#include "dawn/native/DynamicUploader.h"
#include "dawn/native/Instance.h"
#include "dawn/native/d3d/D3DError.h"
#include "dawn/native/d3d/KeyedMutex.h"
#include "dawn/native/d3d/UtilsD3D.h"
#include "dawn/native/d3d11/BackendD3D11.h"
#include "dawn/native/d3d11/BindGroupD3D11.h"
#include "dawn/native/d3d11/BindGroupLayoutD3D11.h"
#include "dawn/native/d3d11/BufferD3D11.h"
#include "dawn/native/d3d11/CommandBufferD3D11.h"
#include "dawn/native/d3d11/ComputePipelineD3D11.h"
#include "dawn/native/d3d11/PhysicalDeviceD3D11.h"
#include "dawn/native/d3d11/PipelineLayoutD3D11.h"
#include "dawn/native/d3d11/PlatformFunctionsD3D11.h"
#include "dawn/native/d3d11/QuerySetD3D11.h"
#include "dawn/native/d3d11/QueueD3D11.h"
#include "dawn/native/d3d11/RenderPipelineD3D11.h"
#include "dawn/native/d3d11/SamplerD3D11.h"
#include "dawn/native/d3d11/ShaderModuleD3D11.h"
#include "dawn/native/d3d11/SharedFenceD3D11.h"
#include "dawn/native/d3d11/SharedTextureMemoryD3D11.h"
#include "dawn/native/d3d11/SwapChainD3D11.h"
#include "dawn/native/d3d11/TextureD3D11.h"
#include "dawn/native/d3d11/UtilsD3D11.h"
#include "dawn/platform/DawnPlatform.h"
#include "dawn/platform/tracing/TraceEvent.h"
namespace dawn::native::d3d11 {
namespace {
static constexpr uint64_t kMaxDebugMessagesToPrint = 5;
bool SkipDebugMessage(const D3D11_MESSAGE& message) {
// Filter out messages that are not warnings or errors.
switch (message.Severity) {
case D3D11_MESSAGE_SEVERITY_INFO:
case D3D11_MESSAGE_SEVERITY_MESSAGE:
return true;
default:
break;
}
switch (message.ID) {
// D3D11 Debug layer warns no RTV set, however it is allowed.
case D3D11_MESSAGE_ID_DEVICE_DRAW_RENDERTARGETVIEW_NOT_SET:
// D3D11 Debug layer warns SetPrivateData() with same name more than once.
case D3D11_MESSAGE_ID_SETPRIVATEDATA_CHANGINGPARAMS:
return true;
default:
return false;
}
}
uint64_t AppendDebugLayerMessagesToError(ID3D11InfoQueue* infoQueue,
uint64_t totalErrors,
ErrorData* error) {
DAWN_ASSERT(totalErrors > 0);
DAWN_ASSERT(error != nullptr);
uint64_t errorsEmitted = 0;
for (uint64_t i = 0; i < totalErrors; ++i) {
std::ostringstream messageStream;
SIZE_T messageLength = 0;
HRESULT hr = infoQueue->GetMessage(i, nullptr, &messageLength);
if (FAILED(hr)) {
messageStream << " ID3D11InfoQueue::GetMessage failed with " << hr;
error->AppendBackendMessage(messageStream.str());
continue;
}
std::unique_ptr<uint8_t[]> messageData(new uint8_t[messageLength]);
D3D11_MESSAGE* message = reinterpret_cast<D3D11_MESSAGE*>(messageData.get());
hr = infoQueue->GetMessage(i, message, &messageLength);
if (FAILED(hr)) {
messageStream << " ID3D11InfoQueue::GetMessage failed with " << hr;
error->AppendBackendMessage(messageStream.str());
continue;
}
if (SkipDebugMessage(*message)) {
continue;
}
messageStream << "(" << message->ID << ") " << message->pDescription;
error->AppendBackendMessage(messageStream.str());
errorsEmitted++;
if (errorsEmitted >= kMaxDebugMessagesToPrint) {
break;
}
}
if (errorsEmitted < totalErrors) {
std::ostringstream messages;
messages << (totalErrors - errorsEmitted) << " messages silenced";
error->AppendBackendMessage(messages.str());
}
// We only print up to the first kMaxDebugMessagesToPrint errors
infoQueue->ClearStoredMessages();
return errorsEmitted;
}
} // namespace
// static
ResultOrError<Ref<Device>> Device::Create(AdapterBase* adapter,
const UnpackedPtr<DeviceDescriptor>& descriptor,
const TogglesState& deviceToggles,
Ref<DeviceBase::DeviceLostEvent>&& lostEvent) {
Ref<Device> device =
AcquireRef(new Device(adapter, descriptor, deviceToggles, std::move(lostEvent)));
DAWN_TRY(device->Initialize(descriptor));
return device;
}
MaybeError Device::Initialize(const UnpackedPtr<DeviceDescriptor>& descriptor) {
DAWN_TRY_ASSIGN(
mD3d11Device,
ToBackend(GetPhysicalDevice())
->CreateD3D11Device(GetAdapter()->GetInstance()->IsBackendValidationEnabled()));
DAWN_ASSERT(mD3d11Device != nullptr);
mIsDebugLayerEnabled = IsDebugLayerEnabled(mD3d11Device);
// Get the ID3D11Device5 interface which is need for creating fences.
// TODO(dawn:1741): Handle the case where ID3D11Device5 is not available.
DAWN_TRY(CheckHRESULT(mD3d11Device.As(&mD3d11Device5), "D3D11: getting ID3D11Device5"));
Ref<Queue> queue;
DAWN_TRY_ASSIGN(queue, Queue::Create(this, &descriptor->defaultQueue));
DAWN_TRY(DeviceBase::Initialize(queue));
DAWN_TRY(queue->InitializePendingContext());
SetLabelImpl();
return {};
}
Device::~Device() = default;
ID3D11Device* Device::GetD3D11Device() const {
return mD3d11Device.Get();
}
ID3D11Device5* Device::GetD3D11Device5() const {
return mD3d11Device5.Get();
}
MaybeError Device::TickImpl() {
// Check for debug layer messages before executing the command context in case we encounter an
// error during execution and early out as a result.
DAWN_TRY(CheckDebugLayerAndGenerateErrors());
DAWN_TRY(ToBackend(GetQueue())->SubmitPendingCommands());
return {};
}
void Device::ReferenceUntilUnused(ComPtr<IUnknown> object) {
mUsedComObjectRefs.Enqueue(object, GetQueue()->GetPendingCommandSerial());
}
ResultOrError<Ref<BindGroupBase>> Device::CreateBindGroupImpl(
const BindGroupDescriptor* descriptor) {
return BindGroup::Create(this, descriptor);
}
ResultOrError<Ref<BindGroupLayoutInternalBase>> Device::CreateBindGroupLayoutImpl(
const BindGroupLayoutDescriptor* descriptor) {
return BindGroupLayout::Create(this, descriptor);
}
ResultOrError<Ref<BufferBase>> Device::CreateBufferImpl(
const UnpackedPtr<BufferDescriptor>& descriptor) {
return Buffer::Create(this, descriptor, /*commandContext=*/nullptr);
}
ResultOrError<Ref<CommandBufferBase>> Device::CreateCommandBuffer(
CommandEncoder* encoder,
const CommandBufferDescriptor* descriptor) {
return CommandBuffer::Create(encoder, descriptor);
}
Ref<ComputePipelineBase> Device::CreateUninitializedComputePipelineImpl(
const UnpackedPtr<ComputePipelineDescriptor>& descriptor) {
return ComputePipeline::CreateUninitialized(this, descriptor);
}
ResultOrError<Ref<PipelineLayoutBase>> Device::CreatePipelineLayoutImpl(
const UnpackedPtr<PipelineLayoutDescriptor>& descriptor) {
return PipelineLayout::Create(this, descriptor);
}
ResultOrError<Ref<QuerySetBase>> Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) {
return QuerySet::Create(this, descriptor);
}
Ref<RenderPipelineBase> Device::CreateUninitializedRenderPipelineImpl(
const UnpackedPtr<RenderPipelineDescriptor>& descriptor) {
return RenderPipeline::CreateUninitialized(this, descriptor);
}
ResultOrError<Ref<SamplerBase>> Device::CreateSamplerImpl(const SamplerDescriptor* descriptor) {
return Sampler::Create(this, descriptor);
}
ResultOrError<Ref<ShaderModuleBase>> Device::CreateShaderModuleImpl(
const UnpackedPtr<ShaderModuleDescriptor>& descriptor,
const std::vector<tint::wgsl::Extension>& internalExtensions,
ShaderModuleParseResult* parseResult,
OwnedCompilationMessages* compilationMessages) {
return ShaderModule::Create(this, descriptor, internalExtensions, parseResult,
compilationMessages);
}
ResultOrError<Ref<SwapChainBase>> Device::CreateSwapChainImpl(Surface* surface,
SwapChainBase* previousSwapChain,
const SurfaceConfiguration* config) {
return SwapChain::Create(this, surface, previousSwapChain, config);
}
ResultOrError<Ref<TextureBase>> Device::CreateTextureImpl(
const UnpackedPtr<TextureDescriptor>& descriptor) {
return Texture::Create(this, descriptor);
}
ResultOrError<Ref<TextureViewBase>> Device::CreateTextureViewImpl(
TextureBase* texture,
const UnpackedPtr<TextureViewDescriptor>& descriptor) {
return TextureView::Create(texture, descriptor);
}
void Device::InitializeComputePipelineAsyncImpl(Ref<CreateComputePipelineAsyncEvent> event) {
event->InitializeAsync();
}
void Device::InitializeRenderPipelineAsyncImpl(Ref<CreateRenderPipelineAsyncEvent> event) {
event->InitializeAsync();
}
ResultOrError<Ref<SharedTextureMemoryBase>> Device::ImportSharedTextureMemoryImpl(
const SharedTextureMemoryDescriptor* descriptor) {
UnpackedPtr<SharedTextureMemoryDescriptor> unpacked;
DAWN_TRY_ASSIGN(unpacked, ValidateAndUnpack(descriptor));
wgpu::SType type;
DAWN_TRY_ASSIGN(
type, (unpacked.ValidateBranches<Branch<SharedTextureMemoryDXGISharedHandleDescriptor>,
Branch<SharedTextureMemoryD3D11Texture2DDescriptor>>()));
switch (type) {
case wgpu::SType::SharedTextureMemoryDXGISharedHandleDescriptor:
DAWN_INVALID_IF(!HasFeature(Feature::SharedTextureMemoryDXGISharedHandle),
"%s is not enabled.",
wgpu::FeatureName::SharedTextureMemoryDXGISharedHandle);
return SharedTextureMemory::Create(
this, descriptor->label,
unpacked.Get<SharedTextureMemoryDXGISharedHandleDescriptor>());
case wgpu::SType::SharedTextureMemoryD3D11Texture2DDescriptor:
DAWN_INVALID_IF(!HasFeature(Feature::SharedTextureMemoryD3D11Texture2D),
"%s is not enabled.",
wgpu::FeatureName::SharedTextureMemoryD3D11Texture2D);
return SharedTextureMemory::Create(
this, descriptor->label,
unpacked.Get<SharedTextureMemoryD3D11Texture2DDescriptor>());
default:
DAWN_UNREACHABLE();
}
}
ResultOrError<Ref<SharedFenceBase>> Device::ImportSharedFenceImpl(
const SharedFenceDescriptor* descriptor) {
UnpackedPtr<SharedFenceDescriptor> unpacked;
DAWN_TRY_ASSIGN(unpacked, ValidateAndUnpack(descriptor));
wgpu::SType type;
DAWN_TRY_ASSIGN(type,
(unpacked.ValidateBranches<Branch<SharedFenceDXGISharedHandleDescriptor>>()));
switch (type) {
case wgpu::SType::SharedFenceDXGISharedHandleDescriptor:
DAWN_INVALID_IF(!HasFeature(Feature::SharedFenceDXGISharedHandle), "%s is not enabled.",
wgpu::FeatureName::SharedFenceDXGISharedHandle);
return SharedFence::Create(this, descriptor->label,
unpacked.Get<SharedFenceDXGISharedHandleDescriptor>());
default:
DAWN_UNREACHABLE();
}
}
MaybeError Device::CopyFromStagingToBufferImpl(BufferBase* source,
uint64_t sourceOffset,
BufferBase* destination,
uint64_t destinationOffset,
uint64_t size) {
// D3D11 requires that buffers are unmapped before being used in a copy.
DAWN_TRY(source->Unmap());
auto commandContext =
ToBackend(GetQueue())->GetScopedPendingCommandContext(QueueBase::SubmitMode::Normal);
return Buffer::Copy(&commandContext, ToBackend(source), sourceOffset, size,
ToBackend(destination), destinationOffset);
}
MaybeError Device::CopyFromStagingToTextureImpl(const BufferBase* source,
const TexelCopyBufferLayout& src,
const TextureCopy& dst,
const Extent3D& copySizePixels) {
return DAWN_UNIMPLEMENTED_ERROR("CopyFromStagingToTextureImpl");
}
const DeviceInfo& Device::GetDeviceInfo() const {
return ToBackend(GetPhysicalDevice())->GetDeviceInfo();
}
MaybeError Device::CheckDebugLayerAndGenerateErrors() {
if (!mIsDebugLayerEnabled) {
return {};
}
ComPtr<ID3D11InfoQueue> infoQueue;
DAWN_TRY(CheckHRESULT(mD3d11Device.As(&infoQueue),
"D3D11 QueryInterface ID3D11Device to ID3D11InfoQueue"));
// We use GetNumStoredMessages instead of applying a retrieval filter because dxcpl.exe
// and d3dconfig.exe override any filter settings we apply.
const uint64_t totalErrors = infoQueue->GetNumStoredMessages();
if (totalErrors == 0) {
return {};
}
auto error = DAWN_INTERNAL_ERROR("The D3D11 debug layer reported uncaught errors.");
const uint64_t emittedErrors =
AppendDebugLayerMessagesToError(infoQueue.Get(), totalErrors, error.get());
if (emittedErrors == 0) {
return {};
}
return error;
}
void Device::AppendDebugLayerMessages(ErrorData* error) {
if (!GetAdapter()->GetInstance()->IsBackendValidationEnabled()) {
return;
}
ComPtr<ID3D11InfoQueue> infoQueue;
if (FAILED(mD3d11Device.As(&infoQueue))) {
return;
}
const uint64_t totalErrors = infoQueue->GetNumStoredMessages();
if (totalErrors == 0) {
return;
}
AppendDebugLayerMessagesToError(infoQueue.Get(), totalErrors, error);
}
void Device::AppendDeviceLostMessage(ErrorData* error) {
if (mD3d11Device) {
HRESULT result = mD3d11Device->GetDeviceRemovedReason();
error->AppendBackendMessage("Device removed reason: %s (0x%08X)",
d3d::HRESULTAsString(result), result);
}
}
void Device::DestroyImpl() {
// TODO(crbug.com/dawn/831): DestroyImpl is called from two places.
// - It may be called if the device is explicitly destroyed with APIDestroy.
// This case is NOT thread-safe and needs proper synchronization with other
// simultaneous uses of the device.
// - It may be called when the last ref to the device is dropped and the device
// is implicitly destroyed. This case is thread-safe because there are no
// other threads using the device since there are no other live refs.
DAWN_ASSERT(GetState() == State::Disconnected);
mImplicitPixelLocalStorageAttachmentTextureViews = {};
mStagingBuffers.clear();
Base::DestroyImpl();
}
uint32_t Device::GetOptimalBytesPerRowAlignment() const {
return 256;
}
uint64_t Device::GetOptimalBufferToTextureCopyOffsetAlignment() const {
return 1;
}
float Device::GetTimestampPeriodInNS() const {
return 1.0f;
}
void Device::SetLabelImpl() {}
void Device::DisposeKeyedMutex(ComPtr<IDXGIKeyedMutex> dxgiKeyedMutex) {
// Nothing to do, the ComPtr will release the keyed mutex.
}
bool Device::MayRequireDuplicationOfIndirectParameters() const {
return true;
}
uint64_t Device::GetBufferCopyOffsetAlignmentForDepthStencil() const {
return DeviceBase::GetBufferCopyOffsetAlignmentForDepthStencil();
}
bool Device::CanTextureLoadResolveTargetInTheSameRenderpass() const {
return true;
}
bool Device::PreferNotUsingMappableOrUniformBufferAsStorage() const {
// D3D11 constant buffer or mappable buffer cannot be used as UAV. Allowing them to be used as
// storage buffer would require some workarounds including extra copies so it's better we
// prefer to not do that.
return true;
}
uint32_t Device::GetUAVSlotCount() const {
return ToBackend(GetPhysicalDevice())->GetUAVSlotCount();
}
ResultOrError<TextureViewBase*> Device::GetOrCreateCachedImplicitPixelLocalStorageAttachment(
uint32_t width,
uint32_t height,
uint32_t implicitAttachmentIndex) {
DAWN_ASSERT(implicitAttachmentIndex <= kMaxPLSSlots);
TextureViewBase* currentAttachmentView =
mImplicitPixelLocalStorageAttachmentTextureViews[implicitAttachmentIndex].Get();
if (currentAttachmentView == nullptr ||
currentAttachmentView->GetTexture()->GetWidth(Aspect::Color) < width ||
currentAttachmentView->GetTexture()->GetHeight(Aspect::Color) < height) {
// Create one 2D texture for each attachment. Note that currently on D3D11 backend we cannot
// create a Texture2D UAV on a 2D array texture with baseArrayLayer > 0 because D3D11
// requires the Unordered Access View dimension declared in the shader code must match the
// view type bound to the Pixel Shader unit, while TEXTURE2D doesn't match TEXTURE2DARRAY.
// TODO(dawn:1703): support 2D array storage textures as implicit pixel local storage
// attachments in WGSL.
TextureDescriptor desc;
desc.dimension = wgpu::TextureDimension::e2D;
desc.format = RenderPipelineBase::kImplicitPLSSlotFormat;
desc.usage = wgpu::TextureUsage::StorageAttachment;
desc.size = {width, height, 1};
Ref<TextureBase> newAttachment;
DAWN_TRY_ASSIGN(newAttachment, CreateTexture(&desc));
DAWN_TRY_ASSIGN(mImplicitPixelLocalStorageAttachmentTextureViews[implicitAttachmentIndex],
newAttachment->CreateView());
}
return mImplicitPixelLocalStorageAttachmentTextureViews[implicitAttachmentIndex].Get();
}
ResultOrError<Ref<BufferBase>> Device::GetStagingBuffer(
const ScopedCommandRecordingContext* commandContext,
uint64_t size) {
constexpr uint64_t kMinStagingBufferSize = 4 * 1024;
uint64_t bufferSize = Align(size, kMinStagingBufferSize);
BufferDescriptor descriptor;
descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
descriptor.size = bufferSize;
descriptor.mappedAtCreation = false;
descriptor.label = "DawnDeviceStagingBuffer";
Ref<BufferBase> buffer;
// We don't cache the buffer if it's too large.
if (bufferSize > kMaxStagingBufferSize) {
DAWN_TRY_ASSIGN(buffer, Buffer::Create(this, Unpack(&descriptor), commandContext,
/*allowUploadBufferEmulation=*/false));
return buffer;
}
ExecutionSerial completedSerial = GetQueue()->GetCompletedCommandSerial();
for (auto it = mStagingBuffers.begin(); it != mStagingBuffers.end(); ++it) {
if ((*it)->GetLastUsageSerial() > completedSerial) {
// This buffer, and none after it are ready. Advance to the end and stop the search.
break;
}
if ((*it)->GetSize() >= bufferSize) {
// this buffer is large enough. Stop searching and remove.
buffer = *it;
mStagingBuffers.erase(it);
return buffer;
}
}
// Create a new staging buffer as no existing one can be re-used.
DAWN_TRY_ASSIGN(buffer, Buffer::Create(this, Unpack(&descriptor), commandContext,
/*allowUploadBufferEmulation=*/false));
mTotalStagingBufferSize += bufferSize;
// Purge the old staging buffers if the total size is too large.
constexpr uint64_t kMaxTotalSize = 16 * 1024 * 1024;
for (auto it = mStagingBuffers.begin(); it != mStagingBuffers.end() &&
mTotalStagingBufferSize > kMaxTotalSize &&
(*it)->GetLastUsageSerial() <= completedSerial;) {
mTotalStagingBufferSize -= (*it)->GetSize();
it = mStagingBuffers.erase(it);
}
return buffer;
}
void Device::ReturnStagingBuffer(Ref<BufferBase>&& buffer) {
DAWN_ASSERT(mStagingBuffers.empty() ||
mStagingBuffers.back()->GetLastUsageSerial() <= buffer->GetLastUsageSerial());
// Only the cached buffers can be re-used.
if (buffer->GetSize() <= kMaxStagingBufferSize) {
mStagingBuffers.push_back(std::move(buffer));
}
}
} // namespace dawn::native::d3d11