// 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 "src/dawn/native/d3d11/DeviceD3D11.h"

#include <algorithm>
#include <limits>
#include <sstream>
#include <utility>

#include "dawn/native/D3D11Backend.h"
#include "dawn/platform/DawnPlatform.h"
#include "src/dawn/common/GPUInfo.h"
#include "src/dawn/native/ChainUtils.h"
#include "src/dawn/native/DynamicUploader.h"
#include "src/dawn/native/Instance.h"
#include "src/dawn/native/d3d/D3DError.h"
#include "src/dawn/native/d3d/KeyedMutex.h"
#include "src/dawn/native/d3d/ShaderUtils.h"
#include "src/dawn/native/d3d/UtilsD3D.h"
#include "src/dawn/native/d3d11/BackendD3D11.h"
#include "src/dawn/native/d3d11/BindGroupD3D11.h"
#include "src/dawn/native/d3d11/BindGroupLayoutD3D11.h"
#include "src/dawn/native/d3d11/BufferD3D11.h"
#include "src/dawn/native/d3d11/CommandBufferD3D11.h"
#include "src/dawn/native/d3d11/CommandRecordingContextD3D11.h"
#include "src/dawn/native/d3d11/ComputePipelineD3D11.h"
#include "src/dawn/native/d3d11/PhysicalDeviceD3D11.h"
#include "src/dawn/native/d3d11/PipelineLayoutD3D11.h"
#include "src/dawn/native/d3d11/PlatformFunctionsD3D11.h"
#include "src/dawn/native/d3d11/QuerySetD3D11.h"
#include "src/dawn/native/d3d11/QueueD3D11.h"
#include "src/dawn/native/d3d11/RenderPipelineD3D11.h"
#include "src/dawn/native/d3d11/SamplerD3D11.h"
#include "src/dawn/native/d3d11/ShaderModuleD3D11.h"
#include "src/dawn/native/d3d11/SharedFenceD3D11.h"
#include "src/dawn/native/d3d11/SharedTextureMemoryD3D11.h"
#include "src/dawn/native/d3d11/SwapChainD3D11.h"
#include "src/dawn/native/d3d11/TextureD3D11.h"
#include "src/dawn/native/d3d11/UtilsD3D11.h"
#include "src/dawn/platform/tracing/TraceEvent.h"
#include "src/utils/compiler.h"

namespace dawn::native::d3d11 {

size_t Sha3CacheFuncs::operator()(const Sha3_256::Output& key) const {
    // This combines the hash of the first 8 bytes and the last 8 bytes of the SHA3-256 output.
    // Given that the randomness of SHA3 is very good across all bits (avalanche effect),
    // sampling from two distant parts of the hash and combining them results in a good hash value
    // suitable for hash table distribution, while being more performant than hashing all 32 bytes.
    return DAWN_UNSAFE_TODO(
        absl::HashOf(absl::MakeSpan(key.data(), 8),
                     absl::MakeSpan(key.data() + Sha3_256::kByteOutputLength - 8, 8)));
}
bool Sha3CacheFuncs::operator()(const Sha3_256::Output& a, const Sha3_256::Output& b) const {
    return DAWN_UNSAFE_TODO(std::memcmp(a.data(), b.data(), sizeof(Sha3_256::Output))) == 0;
}

namespace {

static constexpr uint64_t kMaxDebugMessagesToPrint = 5;

bool SkipDebugMessage(const D3D11_MESSAGE& message) {
    // Filter out messages that are not errors.
    switch (message.Severity) {
        case D3D11_MESSAGE_SEVERITY_INFO:
        case D3D11_MESSAGE_SEVERITY_MESSAGE:
        case D3D11_MESSAGE_SEVERITY_WARNING:
            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;
        case D3D11_MESSAGE_ID_DEVICE_CHECKFEATURESUPPORT_UNRECOGNIZED_FEATURE:
        case D3D11_MESSAGE_ID_DEVICE_CHECKFEATURESUPPORT_INVALIDARG_RETURN:
            // We already handle CheckFeatureSupport() failures so ignore the messages from the
            // debug layer.
            return true;
        case D3D11_MESSAGE_ID_DECODERBEGINFRAME_HAZARD:
            // This is video decoder's error which must happen externally because Dawn doesn't
            // handle video directly. So ignore it.
            return true;
        case D3D11_MESSAGE_ID_DEVICE_DRAWINSTANCED_INSTANCEPOS_OVERFLOW:
        case D3D11_MESSAGE_ID_DEVICE_DRAWINDEXEDINSTANCED_INSTANCEPOS_OVERFLOW:
            // Some clients use workarounds such as packing uniform data in a 32 bits BaseInstance
            // value to avoid needing an uniform buffer. See
            // https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/gpu/graphite/dawn/DawnResourceProvider.cpp;drc=d29622dea776ec762e8a69c8c65b87f8e9ee8908;l=398
            // for one example.
            // The embedded data would cause BaseInstance + InstanceCount to overflow. This is not
            // an error because the workarounds always use InstanceCount=1 and we never invoke any
            // vertex shader at (BaseInstance + InstanceCount)-th instance.
            // Furthermore, the behavior of overflown InstanceID is already well documented in
            // https://learn.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-input-assembler-stage-using#instanceid
            // i.e. it will wrap to 0.
            return true;
        case D3D11_MESSAGE_ID_CREATETEXTURE2D_INVALIDDIMENSIONS:
        case D3D11_MESSAGE_ID_CREATETEXTURE2D_INVALIDARG_RETURN:
            // External video decoder sometimes attempts to create a multiplanar texture with
            // non-even dimensions. This is not supported by D3D11 runtime, but the error should
            // already be handled by the call sites. Chromium's video decoder already does that so
            // it's better we don't treat this as a fatal error.
            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

Device::Device(AdapterBase* adapter,
               const UnpackedPtr<DeviceDescriptor>& descriptor,
               const TogglesState& deviceToggles,
               Ref<DeviceBase::DeviceLostEvent>&& lostEvent)
    : Base(adapter, descriptor, deviceToggles, std::move(lostEvent)),
      mVertexShaderCache(1024),
      mPixelShaderCache(1024),
      mComputeShaderCache(1024) {}

// 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);

    DAWN_TRY(CheckHRESULT(mD3d11Device.As(&mD3d11Device3), "D3D11: getting ID3D11Device3"));

    if (!IsToggleEnabled(Toggle::D3D11DisableFence)) {
        // Get the ID3D11Device5 interface which is need for creating fences. This interface is only
        // available since Win 10 Creators Update so don't return on error here.
        mD3d11Device.As(&mD3d11Device5);
    }

    Ref<Queue> queue;
    DAWN_TRY_ASSIGN(queue, Queue::Create(this, &descriptor->defaultQueue));

    DAWN_TRY(DeviceBase::Initialize(descriptor, queue));
    DAWN_TRY(queue->InitializePendingContext());

    SetLabelImpl();

    return {};
}

Device::~Device() = default;

ID3D11Device* Device::GetD3D11Device() const {
    return mD3d11Device.Get();
}

ID3D11Device3* Device::GetD3D11Device3() const {
    return mD3d11Device3.Get();
}

ID3D11Device5* Device::GetD3D11Device5() const {
    // Some older devices don't support ID3D11Device5. Make sure we avoid calling this method in
    // those cases. An assert here is to verify that.
    DAWN_ASSERT(mD3d11Device5);
    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());
    UnmapDestroyedBuffers();
    return {};
}

void Device::ReferenceUntilUnused(ComPtr<IUnknown> object) {
    mUsedComObjectRefs.Enqueue(object, GetQueue()->GetPendingCommandSerial());
}

ResultOrError<Ref<BindGroupBase>> Device::CreateBindGroupImpl(
    const UnpackedPtr<BindGroupDescriptor>& descriptor) {
    return BindGroup::Create(this, descriptor);
}

ResultOrError<Ref<BindGroupLayoutInternalBase>> Device::CreateBindGroupLayoutImpl(
    const UnpackedPtr<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<ResourceTableBase>> Device::CreateResourceTableImpl(
    const ResourceTableDescriptor* descriptor) {
    return DAWN_UNIMPLEMENTED_ERROR("ResourceTable is not supported on D3D11");
}
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) {
    return ShaderModule::Create(this, descriptor, internalExtensions);
}

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::CopyFromStagingToBuffer(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 scopedUseBuffer = source->UseInternal();

    auto commandContext =
        ToBackend(GetQueue())->GetScopedPendingCommandContext(QueueBase::SubmitMode::Normal);
    return Buffer::Copy(&commandContext, ToBackend(source), sourceOffset, size,
                        ToBackend(destination), destinationOffset);
}

MaybeError Device::CopyFromStagingToTextureImpl(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);
        RecordDeviceRemovedReason(result);
    }
}

void Device::DestroyImpl(DestroyReason reason) {
    // 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(reason);
}

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.
}

void Device::DeferUnmapDestroyedBuffer(ComPtr<ID3D11Buffer> buffer) {
    if (!buffer) {
        return;
    }
    mPendingDestroyedBufferUnmaps->push_back(std::move(buffer));
}

void Device::UnmapDestroyedBuffers() {
    auto commandContext =
        ToBackend(GetQueue())
            ->GetScopedPendingCommandContext(ExecutionQueueBase::SubmitMode::Passive);

    mPendingDestroyedBufferUnmaps.Use([&](auto buffers) {
        for (auto& buffer : *buffers) {
            commandContext.Unmap(buffer.Get(), 0);
        }
        buffers->clear();
    });
}

bool Device::ReduceMemoryUsageImpl() {
    mVertexShaderCache.Clear();
    mPixelShaderCache.Clear();
    mComputeShaderCache.Clear();

    UnmapDestroyedBuffers();

    // D3D11 defers the deletion of resources until we call Flush().
    // So trigger a Flush() here to force deleting any pending resources.
    auto commandContext =
        ToBackend(GetQueue())
            ->GetScopedPendingCommandContext(ExecutionQueueBase::SubmitMode::Passive);
    commandContext.Flush();

    // Call Trim() to delete any internal resources created by the driver.
    ComPtr<IDXGIDevice3> dxgiDevice3;
    if (SUCCEEDED(mD3d11Device.As(&dxgiDevice3))) {
        dxgiDevice3->Trim();
    }

    return false;
}

std::optional<DeviceGuard> Device::UseGuardForCreateBindGroup() {
    return std::nullopt;
}

std::optional<DeviceGuard> Device::UseGuardForCreateBindGroupLayout() {
    return std::nullopt;
}

std::optional<DeviceGuard> Device::UseGuardForCreateBuffer() {
    return std::nullopt;
}

std::optional<DeviceGuard> Device::UseGuardForCreateSampler() {
    return std::nullopt;
}

std::optional<DeviceGuard> Device::UseGuardForCreateTexture() {
    return std::nullopt;
}

bool Device::MayRequireDuplicationOfIndirectParameters() const {
    return true;
}

uint64_t Device::GetBufferCopyOffsetAlignmentForDepthStencil() const {
    return DeviceBase::GetBufferCopyOffsetAlignmentForDepthStencil();
}

bool Device::CanTextureLoadResolveTargetInTheSameRenderpass() const {
    return true;
}

bool Device::CanAddStorageUsageToBufferWithoutSideEffects(wgpu::BufferUsage storageUsage,
                                                          wgpu::BufferUsage originalUsage,
                                                          size_t bufferSize) const {
    return d3d11::CanAddStorageUsageToBufferWithoutSideEffects(this, storageUsage, originalUsage,
                                                               bufferSize);
}

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));
    }
}

ResultOrError<ComPtr<ID3D11VertexShader>> Device::GetOrCreateVertexShader(
    const d3d::CompiledShader& args) {
    return mVertexShaderCache.GetOrCreate(
        args.sha3, [&](const Sha3_256::Output&) -> ResultOrError<ComPtr<ID3D11VertexShader>> {
            ComPtr<ID3D11VertexShader> vs;
            DAWN_TRY(
                CheckHRESULT(mD3d11Device->CreateVertexShader(args.shaderBlob.DataPtr(),
                                                              args.shaderBlob.Size(), nullptr, &vs),
                             "D3D11 create vertex shader"));
            return vs;
        });
}

ResultOrError<ComPtr<ID3D11PixelShader>> Device::GetOrCreatePixelShader(
    const d3d::CompiledShader& args) {
    return mPixelShaderCache.GetOrCreate(
        args.sha3, [&](const Sha3_256::Output&) -> ResultOrError<ComPtr<ID3D11PixelShader>> {
            ComPtr<ID3D11PixelShader> ps;
            DAWN_TRY(
                CheckHRESULT(mD3d11Device->CreatePixelShader(args.shaderBlob.DataPtr(),
                                                             args.shaderBlob.Size(), nullptr, &ps),
                             "D3D11 create pixel shader"));
            return ps;
        });
}

ResultOrError<ComPtr<ID3D11ComputeShader>> Device::GetOrCreateComputeShader(
    const d3d::CompiledShader& args) {
    return mComputeShaderCache.GetOrCreate(
        args.sha3, [&](const Sha3_256::Output&) -> ResultOrError<ComPtr<ID3D11ComputeShader>> {
            ComPtr<ID3D11ComputeShader> cs;
            DAWN_TRY(
                CheckHRESULT(mD3d11Device->CreateComputeShader(
                                 args.shaderBlob.DataPtr(), args.shaderBlob.Size(), nullptr, &cs),
                             "D3D11 create compute shader"));
            return cs;
        });
}

}  // namespace dawn::native::d3d11
