code:
#include "d3d11_cmdlist.h"
#include "d3d11_context_imm.h"
#include "d3d11_device.h"
#include "d3d11_fence.h"
#include "d3d11_texture.h"

constexpr static uint32_t MinFlushIntervalUs = 750;
constexpr static uint32_t IncFlushIntervalUs = 250;
constexpr static uint32_t MaxPendingSubmits  = 6;

namespace dxvk {

  D3D11ImmediateContext::D3D11ImmediateContext(
          D3D11Device*    pParent,
    const Rc<DxvkDevice>& Device)
  : D3D11DeviceContext(pParent, Device, DxvkCsChunkFlag::SingleUse),
    m_csThread(Device, Device->createContext()),
    m_maxImplicitDiscardSize(pParent->GetOptions()->maxImplicitDiscardSize),
    m_videoContext(this, Device) {
    EmitCs([
      cDevice                 = m_device,
      cRelaxedBarriers        = pParent->GetOptions()->relaxedBarriers,
      cIgnoreGraphicsBarriers = pParent->GetOptions()->ignoreGraphicsBarriers
    ] (DxvkContext* ctx) {
      ctx->beginRecording(cDevice->createCommandList());

      DxvkBarrierControlFlags barrierControl;

      if (cRelaxedBarriers)
        barrierControl.set(DxvkBarrierControl::IgnoreWriteAfterWrite);

      if (cIgnoreGraphicsBarriers)
        barrierControl.set(DxvkBarrierControl::IgnoreGraphicsBarriers);

      ctx->setBarrierControl(barrierControl);
    });

    ClearState();
  }

  D3D11ImmediateContext::~D3D11ImmediateContext() {
    // Avoids hanging when in this state, see comment
    // in DxvkDevice::~DxvkDevice.
    if (this_thread::isInModuleDetachment())
      return;

    Flush();
    SynchronizeCsThread(DxvkCsThread::SynchronizeAll);
    SynchronizeDevice();
  }

  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::QueryInterface(REFIID riid, void** ppvObject) {
    if (riid == __uuidof(ID3D11VideoContext)) {
      *ppvObject = ref(&m_videoContext);
      return S_OK;
    }

    return D3D11DeviceContext::QueryInterface(riid, ppvObject);
  }

  D3D11_DEVICE_CONTEXT_TYPE STDMETHODCALLTYPE D3D11ImmediateContext::GetType() {
    return D3D11_DEVICE_CONTEXT_IMMEDIATE;
  }

  UINT STDMETHODCALLTYPE D3D11ImmediateContext::GetContextFlags() {
    return 0;
  }

  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::GetData(
          ID3D11Asynchronous*               pAsync,
          void*                             pData,
          UINT                              DataSize,
          UINT                              GetDataFlags) {
    if (!pAsync || (DataSize && !pData))
      return E_INVALIDARG;

    // Check whether the data size is actually correct
    if (DataSize && DataSize != pAsync->GetDataSize())
      return E_INVALIDARG;

    // Passing a non-null pData is actually allowed if
    // DataSize is 0, but we should ignore that pointer
    pData = DataSize ? pData : nullptr;

    // Get query status directly from the query object
    auto query = static_cast<D3D11Query*>(pAsync);
    HRESULT hr = query->GetData(pData, GetDataFlags);

    // If we're likely going to spin on the asynchronous object,
    // flush the context so that we're keeping the GPU busy.
    if (hr == S_FALSE) {
      // Don't mark the event query as stalling if the app does
      // not intend to spin on it. This reduces flushes on End.
      if (!(GetDataFlags & D3D11_ASYNC_GETDATA_DONOTFLUSH))
        query->NotifyStall();

      // Ignore the DONOTFLUSH flag here as some games will spin
      // on queries without ever flushing the context otherwise.
      FlushImplicit(FALSE);
    }

    return hr;
  }

  void STDMETHODCALLTYPE D3D11ImmediateContext::Begin(ID3D11Asynchronous* pAsync) {
    D3D10DeviceLock lock = LockContext();

    if (unlikely(!pAsync))
      return;

    auto query = static_cast<D3D11Query*>(pAsync);

    if (unlikely(!query->DoBegin()))
      return;

    EmitCs([cQuery = Com<D3D11Query, false>(query)]
    (DxvkContext* ctx) {
      cQuery->Begin(ctx);
    });
  }

  void STDMETHODCALLTYPE D3D11ImmediateContext::End(ID3D11Asynchronous* pAsync) {
    D3D10DeviceLock lock = LockContext();

    if (unlikely(!pAsync))
      return;

    auto query = static_cast<D3D11Query*>(pAsync);

    if (unlikely(!query->DoEnd())) {
      EmitCs([cQuery = Com<D3D11Query, false>(query)]
      (DxvkContext* ctx) {
        cQuery->Begin(ctx);
      });
    }

    EmitCs([cQuery = Com<D3D11Query, false>(query)]
    (DxvkContext* ctx) {
      cQuery->End(ctx);
    });

    if (unlikely(query->TrackStalls())) {
      query->NotifyEnd();

      if (query->IsStalling())
        Flush();
      else if (query->IsEvent())
        FlushImplicit(TRUE);
    }
  }

  void STDMETHODCALLTYPE D3D11ImmediateContext::Flush() {
    Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr);
  }

  void STDMETHODCALLTYPE D3D11ImmediateContext::Flush1(
          D3D11_CONTEXT_TYPE          ContextType,
          HANDLE                      hEvent) {
    m_parent->FlushInitContext();

    if (hEvent)
      SignalEvent(hEvent);

    D3D10DeviceLock lock = LockContext();

    if (m_csIsBusy || !m_csChunk->empty()) {
      // Add commands to flush the threaded
      // context, then flush the command list
      EmitCs([] (DxvkContext* ctx) {
        ctx->flushCommandList();
      });

      FlushCsChunk();

      // Reset flush timer used for implicit flushes
      m_lastFlush = dxvk::high_resolution_clock::now();
      m_csIsBusy  = false;
    }
  }

  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::Signal(
          ID3D11Fence*                pFence,
          UINT64                      Value) {
    auto fence = static_cast<D3D11Fence*>(pFence);

    if (!fence)
      return E_INVALIDARG;

    EmitCs([
      cFence = fence->GetFence(),
      cValue = Value
    ] (DxvkContext* ctx) {
      ctx->signalFence(cFence, cValue);
    });

    Flush();
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::Wait(
          ID3D11Fence*                pFence,
          UINT64                      Value) {
    auto fence = static_cast<D3D11Fence*>(pFence);

    if (!fence)
      return E_INVALIDARG;

    Flush();

    EmitCs([
      cFence = fence->GetFence(),
      cValue = Value
    ] (DxvkContext* ctx) {
      ctx->waitFence(cFence, cValue);
    });

    return S_OK;
  }

  void STDMETHODCALLTYPE D3D11ImmediateContext::ExecuteCommandList(
          ID3D11CommandList*  pCommandList,
          BOOL                RestoreContextState) {
    D3D10DeviceLock lock = LockContext();

    auto commandList = static_cast<D3D11CommandList*>(pCommandList);

    // Flush any outstanding commands so that
    // we don't mess up the execution order
    FlushCsChunk();

    // As an optimization, flush everything if the
    // number of pending draw calls is high enough.
    FlushImplicit(FALSE);

    // Dispatch command list to the CS thread and
    // restore the immediate context's state
    uint64_t csSeqNum = commandList->EmitToCsThread(&m_csThread);
    m_csSeqNum = std::max(m_csSeqNum, csSeqNum);

    if (RestoreContextState)
      RestoreState();
    else
      ClearState();

    // Mark CS thread as busy so that subsequent
    // flush operations get executed correctly.
    m_csIsBusy = true;
  }

  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::FinishCommandList(
          BOOL                RestoreDeferredContextState,
          ID3D11CommandList   **ppCommandList) {
    InitReturnPtr(ppCommandList);

    Logger::err("D3D11: FinishCommandList called on immediate context");
    return DXGI_ERROR_INVALID_CALL;
  }

  HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::Map(
          ID3D11Resource*             pResource,
          UINT                        Subresource,
          D3D11_MAP                   MapType,
          UINT                        MapFlags,
          D3D11_MAPPED_SUBRESOURCE*   pMappedResource) {
    D3D10DeviceLock lock = LockContext();

    if (unlikely(!pResource))
      return E_INVALIDARG;

    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
    pResource->GetType(&resourceDim);

    HRESULT hr;

    if (likely(resourceDim == D3D11_RESOURCE_DIMENSION_BUFFER)) {
      hr = MapBuffer(
        static_cast<D3D11Buffer*>(pResource),
        MapType, MapFlags, pMappedResource);
    } else {
      hr = MapImage(
        GetCommonTexture(pResource),
        Subresource, MapType, MapFlags,
        pMappedResource);
    }

    if (unlikely(FAILED(hr)))
      *pMappedResource = D3D11_MAPPED_SUBRESOURCE();

    return hr;
  }

  void STDMETHODCALLTYPE D3D11ImmediateContext::Unmap(
          ID3D11Resource*             pResource,
          UINT                        Subresource) {
    // Since it is very uncommon for images to be mapped compared
    // to buffers, we count the currently mapped images in order
    // to avoid a virtual method call in the common case.
    if (unlikely(m_mappedImageCount > 0)) {
      D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
      pResource->GetType(&resourceDim);

      if (resourceDim != D3D11_RESOURCE_DIMENSION_BUFFER) {
        D3D10DeviceLock lock = LockContext();
        UnmapImage(GetCommonTexture(pResource), Subresource);
      }
    }
  }

  void STDMETHODCALLTYPE D3D11ImmediateContext::UpdateSubresource(
          ID3D11Resource*                   pDstResource,
          UINT                              DstSubresource,
    const D3D11_BOX*                        pDstBox,
    const void*                             pSrcData,
          UINT                              SrcRowPitch,
          UINT                              SrcDepthPitch) {
    UpdateResource<D3D11ImmediateContext>(this, pDstResource,
      DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch, 0);
  }

  void STDMETHODCALLTYPE D3D11ImmediateContext::UpdateSubresource1(
          ID3D11Resource*                   pDstResource,
          UINT                              DstSubresource,
    const D3D11_BOX*                        pDstBox,
    const void*                             pSrcData,
          UINT                              SrcRowPitch,
          UINT                              SrcDepthPitch,
          UINT                              CopyFlags) {
    UpdateResource<D3D11ImmediateContext>(this, pDstResource,
      DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch, CopyFlags);
  }

  void STDMETHODCALLTYPE D3D11ImmediateContext::OMSetRenderTargets(
          UINT                              NumViews,
          ID3D11RenderTargetView* const*    ppRenderTargetViews,
          ID3D11DepthStencilView*           pDepthStencilView) {
    FlushImplicit(TRUE);

    D3D11DeviceContext::OMSetRenderTargets(
      NumViews, ppRenderTargetViews, pDepthStencilView);
  }

  void STDMETHODCALLTYPE D3D11ImmediateContext::OMSetRenderTargetsAndUnorderedAccessViews(
          UINT                              NumRTVs,
          ID3D11RenderTargetView* const*    ppRenderTargetViews,
          ID3D11DepthStencilView*           pDepthStencilView,
          UINT                              UAVStartSlot,
          UINT                              NumUAVs,
          ID3D11UnorderedAccessView* const* ppUnorderedAccessViews,
    const UINT*                             pUAVInitialCounts) {
    FlushImplicit(TRUE);

    D3D11DeviceContext::OMSetRenderTargetsAndUnorderedAccessViews(
      NumRTVs, ppRenderTargetViews, pDepthStencilView,
      UAVStartSlot, NumUAVs, ppUnorderedAccessViews,
      pUAVInitialCounts);
  }

  HRESULT D3D11ImmediateContext::MapBuffer(
          D3D11Buffer*                pResource,
          D3D11_MAP                   MapType,
          UINT                        MapFlags,
          D3D11_MAPPED_SUBRESOURCE*   pMappedResource) {
    if (unlikely(!pMappedResource))
      return E_INVALIDARG;

    if (unlikely(pResource->GetMapMode() == D3D11_COMMON_BUFFER_MAP_MODE_NONE)) {
      Logger::err("D3D11: Cannot map a device-local buffer");
      return E_INVALIDARG;
    }

    VkDeviceSize bufferSize = pResource->Desc()->ByteWidth;

    if (likely(MapType == D3D11_MAP_WRITE_DISCARD)) {
      // Allocate a new backing slice for the buffer and set
      // it as the 'new' mapped slice. This assumes that the
      // only way to invalidate a buffer is by mapping it.
      auto physSlice = pResource->DiscardSlice();
      pMappedResource->pData      = physSlice.mapPtr;
      pMappedResource->RowPitch   = bufferSize;
      pMappedResource->DepthPitch = bufferSize;

      EmitCs([
        cBuffer      = pResource->GetBuffer(),
        cBufferSlice = physSlice
      ] (DxvkContext* ctx) {
        ctx->invalidateBuffer(cBuffer, cBufferSlice);
      });

      return S_OK;
    } else if (likely(MapType == D3D11_MAP_WRITE_NO_OVERWRITE)) {
      // Put this on a fast path without any extra checks since it's
      // a somewhat desired method to partially update large buffers
      DxvkBufferSliceHandle physSlice = pResource->GetMappedSlice();
      pMappedResource->pData      = physSlice.mapPtr;
      pMappedResource->RowPitch   = bufferSize;
      pMappedResource->DepthPitch = bufferSize;
      return S_OK;
    } else {
      // Quantum Break likes using MAP_WRITE on resources which would force
      // us to synchronize with the GPU multiple times per frame. In those
      // situations, if there are no pending GPU writes to the resource, we
      // can promote it to MAP_WRITE_DISCARD, but preserve the data by doing
      // a CPU copy from the previous buffer slice, to avoid the sync point.
      bool doInvalidatePreserve = false;

      auto buffer = pResource->GetBuffer();
      auto sequenceNumber = pResource->GetSequenceNumber();

      if (MapType != D3D11_MAP_READ && !MapFlags && bufferSize <= m_maxImplicitDiscardSize) {
        SynchronizeCsThread(sequenceNumber);

        bool hasWoAccess = buffer->isInUse(DxvkAccess::Write);
        bool hasRwAccess = buffer->isInUse(DxvkAccess::Read);

        if (hasRwAccess && !hasWoAccess) {
          // Uncached reads can be so slow that a GPU sync may actually be faster
          doInvalidatePreserve = buffer->memFlags() & VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
        }
      }

      if (doInvalidatePreserve) {
        FlushImplicit(TRUE);

        auto prevSlice = pResource->GetMappedSlice();
        auto physSlice = pResource->DiscardSlice();

        EmitCs([
          cBuffer      = std::move(buffer),
          cBufferSlice = physSlice
        ] (DxvkContext* ctx) {
          ctx->invalidateBuffer(cBuffer, cBufferSlice);
        });

        std::memcpy(physSlice.mapPtr, prevSlice.mapPtr, physSlice.length);
        pMappedResource->pData      = physSlice.mapPtr;
        pMappedResource->RowPitch   = bufferSize;
        pMappedResource->DepthPitch = bufferSize;
        return S_OK;
      } else {
        if (!WaitForResource(buffer, sequenceNumber, MapType, MapFlags))
          return DXGI_ERROR_WAS_STILL_DRAWING;

        DxvkBufferSliceHandle physSlice = pResource->GetMappedSlice();
        pMappedResource->pData      = physSlice.mapPtr;
        pMappedResource->RowPitch   = bufferSize;
        pMappedResource->DepthPitch = bufferSize;
        return S_OK;
      }
    }
  }

  HRESULT D3D11ImmediateContext::MapImage(
          D3D11CommonTexture*         pResource,
          UINT                        Subresource,
          D3D11_MAP                   MapType,
          UINT                        MapFlags,
          D3D11_MAPPED_SUBRESOURCE*   pMappedResource) {
    const Rc<DxvkImage>  mappedImage  = pResource->GetImage();
    const Rc<DxvkBuffer> mappedBuffer = pResource->GetMappedBuffer(Subresource);

    auto mapMode = pResource->GetMapMode();

    if (unlikely(mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_NONE)) {
      Logger::err("D3D11: Cannot map a device-local image");
      return E_INVALIDARG;
    }

    if (unlikely(Subresource >= pResource->CountSubresources()))
      return E_INVALIDARG;

    if (likely(pMappedResource != nullptr)) {
      // Resources with an unknown memory layout cannot return a pointer
      if (pResource->Desc()->Usage         == D3D11_USAGE_DEFAULT
       && pResource->Desc()->TextureLayout == D3D11_TEXTURE_LAYOUT_UNDEFINED)
        return E_INVALIDARG;
    } else {
      if (pResource->Desc()->Usage != D3D11_USAGE_DEFAULT)
        return E_INVALIDARG;
    }

    VkFormat packedFormat = m_parent->LookupPackedFormat(
      pResource->Desc()->Format, pResource->GetFormatMode()).Format;

    uint64_t sequenceNumber = pResource->GetSequenceNumber(Subresource);

    auto formatInfo = imageFormatInfo(packedFormat);
    void* mapPtr;

    if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) {
      // Wait for the resource to become available. We do not
      // support image renaming, so stall on DISCARD instead.
      if (MapType == D3D11_MAP_WRITE_DISCARD)
        MapFlags &= ~D3D11_MAP_FLAG_DO_NOT_WAIT;

      if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE) {
        if (!WaitForResource(mappedImage, sequenceNumber, MapType, MapFlags))
          return DXGI_ERROR_WAS_STILL_DRAWING;
      }

      // Query the subresource's memory layout and hope that
      // the application respects the returned pitch values.
      mapPtr = mappedImage->mapPtr(0);
    } else {
      constexpr uint32_t DoInvalidate = (1u << 0);
      constexpr uint32_t DoPreserve   = (1u << 1);
      constexpr uint32_t DoWait       = (1u << 2);
      uint32_t doFlags;

      if (MapType == D3D11_MAP_READ) {
        // Reads will not change the image content, so we only need
        // to wait for the GPU to finish writing to the mapped buffer.
        doFlags = DoWait;
      } else if (MapType == D3D11_MAP_WRITE_DISCARD) {
        doFlags = DoInvalidate;

        // If we know for sure that the mapped buffer is currently not
        // in use by the GPU, we don't have to allocate a new slice.
        if (m_csThread.lastSequenceNumber() >= sequenceNumber && !mappedBuffer->isInUse(DxvkAccess::Read))
          doFlags = 0;
      } else if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING && (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT)) {
        // Always respect DO_NOT_WAIT for mapped staging images
        doFlags = DoWait;
      } else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE || mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER) {
        // Need to synchronize thread to determine pending GPU accesses
        SynchronizeCsThread(sequenceNumber);

        // Don't implicitly discard large buffers or buffers of images with
        // multiple subresources, as that is likely to cause memory issues.
        VkDeviceSize bufferSize = pResource->GetMappedSlice(Subresource).length;

        if (bufferSize >= m_maxImplicitDiscardSize || pResource->CountSubresources() > 1) {
          // Don't check access flags, WaitForResource will return
          // early anyway if the resource is currently in use
          doFlags = DoWait;
        } else if (mappedBuffer->isInUse(DxvkAccess::Write)) {
          // There are pending GPU writes, need to wait for those
          doFlags = DoWait;
        } else if (mappedBuffer->isInUse(DxvkAccess::Read)) {
          // All pending GPU accesses are reads, so the buffer data
          // is still current, and we can prevent GPU synchronization
          // by creating a new slice with an exact copy of the data.
          doFlags = DoInvalidate | DoPreserve;
        } else {
          // There are no pending accesses, so we don't need to wait
          doFlags = 0;
        }
      } else {
        // No need to synchronize staging resources with NO_OVERWRITE
        // since the buffer will be used directly.
        doFlags = 0;
      }

      if (doFlags & DoInvalidate) {
        FlushImplicit(TRUE);

        DxvkBufferSliceHandle prevSlice = pResource->GetMappedSlice(Subresource);
        DxvkBufferSliceHandle physSlice = pResource->DiscardSlice(Subresource);

        EmitCs([
          cImageBuffer = mappedBuffer,
          cBufferSlice = physSlice
        ] (DxvkContext* ctx) {
          ctx->invalidateBuffer(cImageBuffer, cBufferSlice);
        });

        if (doFlags & DoPreserve)
          std::memcpy(physSlice.mapPtr, prevSlice.mapPtr, physSlice.length);

        mapPtr = physSlice.mapPtr;
      } else {
        if (doFlags & DoWait) {
          // We cannot respect DO_NOT_WAIT for buffer-mapped resources since
          // our internal copies need to be transparent to the application.
          if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER)
            MapFlags &= ~D3D11_MAP_FLAG_DO_NOT_WAIT;

          // Wait for mapped buffer to become available
          if (!WaitForResource(mappedBuffer, sequenceNumber, MapType, MapFlags))
            return DXGI_ERROR_WAS_STILL_DRAWING;
        }

        mapPtr = pResource->GetMappedSlice(Subresource).mapPtr;
      }
    }

    // Mark the given subresource as mapped
    pResource->SetMapType(Subresource, MapType);

    if (pMappedResource) {
      auto layout = pResource->GetSubresourceLayout(formatInfo->aspectMask, Subresource);
      pMappedResource->pData      = reinterpret_cast<char*>(mapPtr) + layout.Offset;
      pMappedResource->RowPitch   = layout.RowPitch;
      pMappedResource->DepthPitch = layout.DepthPitch;
    }

    m_mappedImageCount += 1;
    return S_OK;
  }

  void D3D11ImmediateContext::UnmapImage(
          D3D11CommonTexture*         pResource,
          UINT                        Subresource) {
    D3D11_MAP mapType = pResource->GetMapType(Subresource);
    pResource->SetMapType(Subresource, D3D11_MAP(~0u));

    if (mapType == D3D11_MAP(~0u))
      return;

    // Decrement mapped image counter only after making sure
    // the given subresource is actually mapped right now
    m_mappedImageCount -= 1;

    if ((mapType != D3D11_MAP_READ) &&
        (pResource->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER)) {
      // Now that data has been written into the buffer,
      // we need to copy its contents into the image
      VkImageAspectFlags aspectMask = imageFormatInfo(pResource->GetPackedFormat())->aspectMask;
      VkImageSubresource subresource = pResource->GetSubresourceFromIndex(aspectMask, Subresource);

      UpdateImage(pResource, &subresource, VkOffset3D { 0, 0, 0 },
        pResource->MipLevelExtent(subresource.mipLevel),
        DxvkBufferSlice(pResource->GetMappedBuffer(Subresource)));
    }
  }

  void D3D11ImmediateContext::UpdateMappedBuffer(
          D3D11Buffer*                  pDstBuffer,
          UINT                          Offset,
          UINT                          Length,
    const void*                         pSrcData,
          UINT                          CopyFlags) {
    DxvkBufferSliceHandle slice;

    if (likely(CopyFlags != D3D11_COPY_NO_OVERWRITE)) {
      slice = pDstBuffer->DiscardSlice();

      EmitCs([
        cBuffer      = pDstBuffer->GetBuffer(),
        cBufferSlice = slice
      ] (DxvkContext* ctx) {
        ctx->invalidateBuffer(cBuffer, cBufferSlice);
      });
    } else {
      slice = pDstBuffer->GetMappedSlice();
    }

    std::memcpy(reinterpret_cast<char*>(slice.mapPtr) + Offset, pSrcData, Length);
  }

  void STDMETHODCALLTYPE D3D11ImmediateContext::SwapDeviceContextState(
          ID3DDeviceContextState*           pState,
          ID3DDeviceContextState**          ppPreviousState) {
    InitReturnPtr(ppPreviousState);

    if (!pState)
      return;

    Com<D3D11DeviceContextState> oldState = std::move(m_stateObject);
    Com<D3D11DeviceContextState> newState = static_cast<D3D11DeviceContextState*>(pState);

    if (oldState == nullptr)
      oldState = new D3D11DeviceContextState(m_parent);

    if (ppPreviousState)
      *ppPreviousState = oldState.ref();

    m_stateObject = newState;

    oldState->SetState(m_state);
    newState->GetState(m_state);

    RestoreState();
  }

  void D3D11ImmediateContext::SynchronizeCsThread(uint64_t SequenceNumber) {
    D3D10DeviceLock lock = LockContext();

    // Dispatch current chunk so that all commands
    // recorded prior to this function will be run
    if (SequenceNumber > m_csSeqNum)
      FlushCsChunk();

    m_csThread.synchronize(SequenceNumber);
  }

  void D3D11ImmediateContext::SynchronizeDevice() {
    m_device->waitForIdle();
  }

  bool D3D11ImmediateContext::WaitForResource(
    const Rc<DxvkResource>&                 Resource,
          uint64_t                          SequenceNumber,
          D3D11_MAP                         MapType,
          UINT                              MapFlags) {
    // Determine access type to wait for based on map mode
    DxvkAccess access = MapType == D3D11_MAP_READ
      ? DxvkAccess::Write
      : DxvkAccess::Read;

    // Wait for any CS chunk using the resource to execute, since
    // otherwise we cannot accurately determine if the resource is
    // actually being used by the GPU right now.
    bool isInUse = Resource->isInUse(access);

    if (!isInUse) {
      SynchronizeCsThread(SequenceNumber);
      isInUse = Resource->isInUse(access);
    }

    if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT) {
      if (isInUse) {
        // We don't have to wait, but misbehaving games may
        // still try to spin on `Map` until the resource is
        // idle, so we should flush pending commands
        FlushImplicit(FALSE);
        return false;
      }
    } else {
      if (isInUse) {
        // Make sure pending commands using the resource get
        // executed on the the GPU if we have to wait for it
        Flush();
        SynchronizeCsThread(SequenceNumber);

        m_device->waitForResource(Resource, access);
      }
    }

    return true;
  }

  void D3D11ImmediateContext::EmitCsChunk(DxvkCsChunkRef&& chunk) {
    m_csSeqNum = m_csThread.dispatchChunk(std::move(chunk));
    m_csIsBusy = true;
  }

  void D3D11ImmediateContext::TrackTextureSequenceNumber(
          D3D11CommonTexture*         pResource,
          UINT                        Subresource) {
    uint64_t sequenceNumber = GetCurrentSequenceNumber();
    pResource->TrackSequenceNumber(Subresource, sequenceNumber);

    FlushImplicit(TRUE);
  }

  void D3D11ImmediateContext::TrackBufferSequenceNumber(
          D3D11Buffer*                pResource) {
    uint64_t sequenceNumber = GetCurrentSequenceNumber();
    pResource->TrackSequenceNumber(sequenceNumber);

    FlushImplicit(TRUE);
  }

  uint64_t D3D11ImmediateContext::GetCurrentSequenceNumber() {
    // We do not flush empty chunks, so if we are tracking a resource
    // immediately after a flush, we need to use the sequence number
    // of the previously submitted chunk to prevent deadlocks.
    return m_csChunk->empty() ? m_csSeqNum : m_csSeqNum + 1;
  }

  void D3D11ImmediateContext::FlushImplicit(BOOL StrongHint) {
    // Flush only if the GPU is about to go idle, in
    // order to keep the number of submissions low.
    uint32_t pending = m_device->pendingSubmissions();

    if (StrongHint || pending <= MaxPendingSubmits) {
      auto now = dxvk::high_resolution_clock::now();

      uint32_t delay = MinFlushIntervalUs
                     + IncFlushIntervalUs * pending;

      // Prevent flushing too often in short intervals.
      if (now - m_lastFlush >= std::chrono::microseconds(delay))
        Flush();
    }
  }

  void D3D11ImmediateContext::SignalEvent(HANDLE hEvent) {
    uint64_t value = ++m_eventCount;

    if (m_eventSignal == nullptr)
      m_eventSignal = new sync::CallbackFence();

    m_eventSignal->setCallback(value, [hEvent] {
      SetEvent(hEvent);
    });

    EmitCs([
      cSignal = m_eventSignal,
      cValue  = value
    ] (DxvkContext* ctx) {
      ctx->signal(cSignal, cValue);
    });
  }

}


code:
#pragma once

#include "../util/util_time.h"

#include "../util/sync/sync_signal.h"

#include "d3d11_context.h"
#include "d3d11_state_object.h"
#include "d3d11_video.h"

namespace dxvk {

  class D3D11Buffer;
  class D3D11CommonTexture;

  class D3D11ImmediateContext : public D3D11DeviceContext {
    friend class D3D11SwapChain;
    friend class D3D11VideoContext;
    friend class D3D11DeviceContext;
  public:

    D3D11ImmediateContext(
            D3D11Device*    pParent,
      const Rc<DxvkDevice>& Device);
    ~D3D11ImmediateContext();

    HRESULT STDMETHODCALLTYPE QueryInterface(
            REFIID  riid,
            void**  ppvObject);

    D3D11_DEVICE_CONTEXT_TYPE STDMETHODCALLTYPE GetType();

    UINT STDMETHODCALLTYPE GetContextFlags();

    HRESULT STDMETHODCALLTYPE GetData(
            ID3D11Asynchronous*         pAsync,
            void*                       pData,
            UINT                        DataSize,
            UINT                        GetDataFlags);

    void STDMETHODCALLTYPE Begin(
            ID3D11Asynchronous*         pAsync);

    void STDMETHODCALLTYPE End(
            ID3D11Asynchronous*         pAsync);

    void STDMETHODCALLTYPE Flush();

    void STDMETHODCALLTYPE Flush1(
            D3D11_CONTEXT_TYPE          ContextType,
            HANDLE                      hEvent);

    HRESULT STDMETHODCALLTYPE Signal(
            ID3D11Fence*                pFence,
            UINT64                      Value);

    HRESULT STDMETHODCALLTYPE Wait(
            ID3D11Fence*                pFence,
            UINT64                      Value);

    void STDMETHODCALLTYPE ExecuteCommandList(
            ID3D11CommandList*  pCommandList,
            BOOL                RestoreContextState);

    HRESULT STDMETHODCALLTYPE FinishCommandList(
            BOOL                RestoreDeferredContextState,
            ID3D11CommandList   **ppCommandList);

    HRESULT STDMETHODCALLTYPE Map(
            ID3D11Resource*             pResource,
            UINT                        Subresource,
            D3D11_MAP                   MapType,
            UINT                        MapFlags,
            D3D11_MAPPED_SUBRESOURCE*   pMappedResource);

    void STDMETHODCALLTYPE Unmap(
            ID3D11Resource*             pResource,
            UINT                        Subresource);

    void STDMETHODCALLTYPE UpdateSubresource(
            ID3D11Resource*                   pDstResource,
            UINT                              DstSubresource,
      const D3D11_BOX*                        pDstBox,
      const void*                             pSrcData,
            UINT                              SrcRowPitch,
            UINT                              SrcDepthPitch);

    void STDMETHODCALLTYPE UpdateSubresource1(
            ID3D11Resource*                   pDstResource,
            UINT                              DstSubresource,
      const D3D11_BOX*                        pDstBox,
      const void*                             pSrcData,
            UINT                              SrcRowPitch,
            UINT                              SrcDepthPitch,
            UINT                              CopyFlags);

    void STDMETHODCALLTYPE OMSetRenderTargets(
            UINT                              NumViews,
            ID3D11RenderTargetView* const*    ppRenderTargetViews,
            ID3D11DepthStencilView*           pDepthStencilView);

    void STDMETHODCALLTYPE OMSetRenderTargetsAndUnorderedAccessViews(
            UINT                              NumRTVs,
            ID3D11RenderTargetView* const*    ppRenderTargetViews,
            ID3D11DepthStencilView*           pDepthStencilView,
            UINT                              UAVStartSlot,
            UINT                              NumUAVs,
            ID3D11UnorderedAccessView* const* ppUnorderedAccessViews,
      const UINT*                             pUAVInitialCounts);

    void STDMETHODCALLTYPE SwapDeviceContextState(
            ID3DDeviceContextState*           pState,
            ID3DDeviceContextState**          ppPreviousState);

    void SynchronizeCsThread(
            uint64_t                          SequenceNumber);

  private:

    DxvkCsThread            m_csThread;
    uint64_t                m_csSeqNum = 0ull;
    bool                    m_csIsBusy = false;

    Rc<sync::CallbackFence> m_eventSignal;
    uint64_t                m_eventCount = 0ull;
    uint32_t                m_mappedImageCount = 0u;

    VkDeviceSize            m_maxImplicitDiscardSize = 0ull;

    dxvk::high_resolution_clock::time_point m_lastFlush
      = dxvk::high_resolution_clock::now();

    D3D11VideoContext            m_videoContext;
    Com<D3D11DeviceContextState> m_stateObject;

    HRESULT MapBuffer(
            D3D11Buffer*                pResource,
            D3D11_MAP                   MapType,
            UINT                        MapFlags,
            D3D11_MAPPED_SUBRESOURCE*   pMappedResource);

    HRESULT MapImage(
            D3D11CommonTexture*         pResource,
            UINT                        Subresource,
            D3D11_MAP                   MapType,
            UINT                        MapFlags,
            D3D11_MAPPED_SUBRESOURCE*   pMappedResource);

    void UnmapImage(
            D3D11CommonTexture*         pResource,
            UINT                        Subresource);

    void UpdateMappedBuffer(
            D3D11Buffer*                  pDstBuffer,
            UINT                          Offset,
            UINT                          Length,
      const void*                         pSrcData,
            UINT                          CopyFlags);

    void SynchronizeDevice();

    bool WaitForResource(
      const Rc<DxvkResource>&                 Resource,
            uint64_t                          SequenceNumber,
            D3D11_MAP                         MapType,
            UINT                              MapFlags);

    void EmitCsChunk(DxvkCsChunkRef&& chunk);

    void TrackTextureSequenceNumber(
            D3D11CommonTexture*         pResource,
            UINT                        Subresource);

    void TrackBufferSequenceNumber(
            D3D11Buffer*                pResource);

    uint64_t GetCurrentSequenceNumber();

    void FlushImplicit(BOOL StrongHint);

    void SignalEvent(HANDLE hEvent);

  };

}

repo_name: orochi/dxvk


code:
#pragma once

#include <array>

#include "d3d11_buffer.h"
#include "d3d11_input_layout.h"
#include "d3d11_query.h"
#include "d3d11_sampler.h"
#include "d3d11_shader.h"
#include "d3d11_state.h"
#include "d3d11_view_dsv.h"
#include "d3d11_view_rtv.h"
#include "d3d11_view_srv.h"
#include "d3d11_view_uav.h"

namespace dxvk {

  struct D3D11ConstantBufferBinding {
    Com<D3D11Buffer> buffer         = nullptr;
    UINT             constantOffset = 0;
    UINT             constantCount  = 0;
    UINT             constantBound  = 0;
  };

  using D3D11ConstantBufferBindings = std::array<
    D3D11ConstantBufferBinding, D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT>;

  using D3D11SamplerBindings = std::array<
    D3D11SamplerState*, D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT>;

  struct D3D11ShaderResourceBindings {
    std::array<Com<D3D11ShaderResourceView>, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT> views     = { };
    DxvkBindingSet<D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT>                           hazardous = { };
  };

  using D3D11UnorderedAccessBindings = std::array<
    Com<D3D11UnorderedAccessView>, D3D11_1_UAV_SLOT_COUNT>;

  struct D3D11ContextStateVS {
    Com<D3D11VertexShader>        shader = nullptr;
    D3D11ConstantBufferBindings   constantBuffers = { };
    D3D11SamplerBindings          samplers        = { };
    D3D11ShaderResourceBindings   shaderResources = { };
  };

  struct D3D11ContextStateHS {
    Com<D3D11HullShader>          shader = nullptr;
    D3D11ConstantBufferBindings   constantBuffers = { };
    D3D11SamplerBindings          samplers        = { };
    D3D11ShaderResourceBindings   shaderResources = { };
  };

  struct D3D11ContextStateDS {
    Com<D3D11DomainShader>        shader = nullptr;
    D3D11ConstantBufferBindings   constantBuffers = { };
    D3D11SamplerBindings          samplers        = { };
    D3D11ShaderResourceBindings   shaderResources = { };
  };

  struct D3D11ContextStateGS {
    Com<D3D11GeometryShader>      shader = nullptr;
    D3D11ConstantBufferBindings   constantBuffers = { };
    D3D11SamplerBindings          samplers        = { };
    D3D11ShaderResourceBindings   shaderResources = { };
  };

  struct D3D11ContextStatePS {
    Com<D3D11PixelShader>         shader = nullptr;
    D3D11ConstantBufferBindings   constantBuffers = { };
    D3D11SamplerBindings          samplers        = { };
    D3D11ShaderResourceBindings   shaderResources = { };
    D3D11UnorderedAccessBindings  unorderedAccessViews = { };
  };

  struct D3D11ContextStateCS {
    Com<D3D11ComputeShader>       shader = nullptr;
    D3D11ConstantBufferBindings   constantBuffers = { };
    D3D11SamplerBindings          samplers        = { };
    D3D11ShaderResourceBindings   shaderResources = { };
    D3D11UnorderedAccessBindings  unorderedAccessViews = { };

    DxvkBindingSet<D3D11_1_UAV_SLOT_COUNT> uavMask = { };
  };

  struct D3D11VertexBufferBinding {
    Com<D3D11Buffer> buffer = nullptr;
    UINT             offset = 0;
    UINT             stride = 0;
  };

  struct D3D11IndexBufferBinding {
    Com<D3D11Buffer> buffer = nullptr;
    UINT             offset = 0;
    DXGI_FORMAT      format = DXGI_FORMAT_UNKNOWN;
  };

  struct D3D11ContextStateID {
    Com<D3D11Buffer> argBuffer = nullptr;
    Com<D3D11Buffer> cntBuffer = nullptr;
  };

  struct D3D11ContextStateIA {
    Com<D3D11InputLayout>    inputLayout       = nullptr;
    D3D11_PRIMITIVE_TOPOLOGY primitiveTopology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;

    std::array<D3D11VertexBufferBinding, D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT> vertexBuffers = { };
    D3D11IndexBufferBinding                                                         indexBuffer   = { };
  };

  struct D3D11ContextStateOM {
    std::array<Com<D3D11RenderTargetView, false>, D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT> renderTargetViews = { };
    Com<D3D11DepthStencilView, false>                                                     depthStencilView  = { };

    D3D11BlendState*        cbState = nullptr;
    D3D11DepthStencilState* dsState = nullptr;

    FLOAT blendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
    UINT  sampleMask     = 0xFFFFFFFFu;
    UINT  stencilRef     = 0u;

    UINT  maxRtv         = 0u;
    UINT  maxUav         = 0u;
  };

  struct D3D11ContextStateRS {
    uint32_t numViewports = 0;
    uint32_t numScissors  = 0;

    std::array<D3D11_VIEWPORT, D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE> viewports = { };
    std::array<D3D11_RECT,     D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE> scissors  = { };

    D3D11RasterizerState* state = nullptr;
  };

  struct D3D11ContextSoTarget {
    Com<D3D11Buffer> buffer = nullptr;
    UINT             offset = 0;
  };

  struct D3D11ContextStateSO {
    std::array<D3D11ContextSoTarget, D3D11_SO_BUFFER_SLOT_COUNT> targets = { };
  };

  struct D3D11ContextStatePR {
    Com<D3D11Query> predicateObject = nullptr;
    BOOL            predicateValue  = FALSE;
  };

  /**
   * \brief Context state
   */
  struct D3D11ContextState {
    D3D11ContextStateCS cs;
    D3D11ContextStateDS ds;
    D3D11ContextStateGS gs;
    D3D11ContextStateHS hs;
    D3D11ContextStatePS ps;
    D3D11ContextStateVS vs;

    D3D11ContextStateID id;
    D3D11ContextStateIA ia;
    D3D11ContextStateOM om;
    D3D11ContextStateRS rs;
    D3D11ContextStateSO so;
    D3D11ContextStatePR pr;
  };

}


code:
#include "d3d11_cuda.h"

namespace dxvk {

  CubinShaderWrapper::CubinShaderWrapper(const Rc<dxvk::DxvkDevice>& dxvkDevice, VkCuModuleNVX cuModule, VkCuFunctionNVX cuFunction, VkExtent3D blockDim)
  : m_dxvkDevice(dxvkDevice), m_module(cuModule), m_function(cuFunction), m_blockDim(blockDim) { };

  CubinShaderWrapper::~CubinShaderWrapper() {
    VkDevice vkDevice = m_dxvkDevice->handle();
    m_dxvkDevice->vkd()->vkDestroyCuFunctionNVX(vkDevice, m_function, nullptr);
    m_dxvkDevice->vkd()->vkDestroyCuModuleNVX(vkDevice, m_module, nullptr);
  };

  HRESULT STDMETHODCALLTYPE CubinShaderWrapper::QueryInterface(REFIID riid, void **ppvObject) {
    if (riid == __uuidof(IUnknown)) {
      *ppvObject = ref(this);
      return S_OK;
    }

    Logger::warn("CubinShaderWrapper::QueryInterface: Unknown interface query");
    Logger::warn(str::format(riid));
    return E_NOINTERFACE;
  }

  void CubinShaderLaunchInfo::insertResource(ID3D11Resource* pResource, DxvkAccessFlags access) {
    auto img = GetCommonTexture(pResource);
    auto buf = GetCommonBuffer(pResource);

    if (img)
      insertUniqueResource(images, img->GetImage(), access);
    if (buf)
      insertUniqueResource(buffers, buf->GetBuffer(), access);
  }

  template<typename T>
  void CubinShaderLaunchInfo::insertUniqueResource(std::vector<std::pair<T, DxvkAccessFlags>>& list, const T& resource, DxvkAccessFlags access) {
    for (auto& entry : list) {
      if (entry.first == resource) {
        entry.second.set(access);
        return;
      }
    }

    list.push_back({ resource, access });
  }

}


code:
#pragma once

#include <utility>
#include <vector>

#include "../dxvk/dxvk_resource.h"

#include "../util/com/com_guid.h"
#include "../util/com/com_object.h"

#include "d3d11_buffer.h"
#include "d3d11_texture.h"

namespace dxvk {

  class CubinShaderWrapper : public ComObject<IUnknown> {

  public:

    CubinShaderWrapper(const Rc<dxvk::DxvkDevice>& dxvkDevice, VkCuModuleNVX cuModule, VkCuFunctionNVX cuFunction, VkExtent3D blockDim);
    ~CubinShaderWrapper();

    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);

    VkCuModuleNVX cuModule() const {
      return m_module;
    }

    VkCuFunctionNVX cuFunction() const {
      return m_function;
    }

    VkExtent3D blockDim() const {
      return m_blockDim;
    }

  private:

    Rc<DxvkDevice>  m_dxvkDevice;
    VkCuModuleNVX   m_module;
    VkCuFunctionNVX m_function;
    VkExtent3D      m_blockDim;

  };

  struct CubinShaderLaunchInfo {

    CubinShaderLaunchInfo() = default;

    CubinShaderLaunchInfo(CubinShaderLaunchInfo&& other) {
      shader         = std::move(other.shader);
      params         = std::move(other.params);
      paramSize      = std::move(other.paramSize);
      nvxLaunchInfo  = std::move(other.nvxLaunchInfo);
      cuLaunchConfig = other.cuLaunchConfig;
      buffers        = std::move(other.buffers);
      images         = std::move(other.images);
      other.cuLaunchConfig[1] = nullptr;
      other.cuLaunchConfig[3] = nullptr;
      other.nvxLaunchInfo.pExtras = nullptr;
      // fix-up internally-pointing pointers
      cuLaunchConfig[1] = params.data();
      cuLaunchConfig[3] = &paramSize;
      nvxLaunchInfo.pExtras = cuLaunchConfig.data();
    }

    Com<CubinShaderWrapper> shader;
    std::vector<uint8_t>    params;
    size_t                  paramSize;
    VkCuLaunchInfoNVX       nvxLaunchInfo = { VK_STRUCTURE_TYPE_CU_LAUNCH_INFO_NVX };
    std::array<void*, 5>    cuLaunchConfig;

    std::vector<std::pair<Rc<DxvkBuffer>, DxvkAccessFlags>> buffers;
    std::vector<std::pair<Rc<DxvkImage>, DxvkAccessFlags>> images;

    void insertResource(ID3D11Resource* pResource, DxvkAccessFlags access);

    template<typename T>
    static void insertUniqueResource(std::vector<std::pair<T, DxvkAccessFlags>>& list, const T& resource, DxvkAccessFlags access);
  };

}


code:
#include "d3d11_depth_stencil.h"
#include "d3d11_device.h"

namespace dxvk {

  D3D11DepthStencilState::D3D11DepthStencilState(
          D3D11Device*              device,
    const D3D11_DEPTH_STENCIL_DESC& desc)
  : D3D11StateObject<ID3D11DepthStencilState>(device),
    m_desc(desc), m_d3d10(this) {
    m_state.enableDepthTest   = desc.DepthEnable;
    m_state.enableDepthWrite  = desc.DepthWriteMask == D3D11_DEPTH_WRITE_MASK_ALL;
    m_state.enableStencilTest = desc.StencilEnable;
    m_state.depthCompareOp    = DecodeCompareOp(desc.DepthFunc);
    m_state.stencilOpFront    = DecodeStencilOpState(desc.FrontFace, desc);
    m_state.stencilOpBack     = DecodeStencilOpState(desc.BackFace,  desc);
  }

  D3D11DepthStencilState::~D3D11DepthStencilState() {

  }

  HRESULT STDMETHODCALLTYPE D3D11DepthStencilState::QueryInterface(REFIID riid, void** ppvObject) {
    if (ppvObject == nullptr)
      return E_POINTER;

    *ppvObject = nullptr;

    if (riid == __uuidof(IUnknown)
     || riid == __uuidof(ID3D11DeviceChild)
     || riid == __uuidof(ID3D11DepthStencilState)) {
      *ppvObject = ref(this);
      return S_OK;
    }

    if (riid == __uuidof(ID3D10DeviceChild)
     || riid == __uuidof(ID3D10DepthStencilState)) {
      *ppvObject = ref(this);
      return S_OK;
    }

    Logger::warn("D3D11DepthStencilState::QueryInterface: Unknown interface query");
    Logger::warn(str::format(riid));
    return E_NOINTERFACE;
  }

  void STDMETHODCALLTYPE D3D11DepthStencilState::GetDesc(D3D11_DEPTH_STENCIL_DESC* pDesc) {
    *pDesc = m_desc;
  }

  void D3D11DepthStencilState::BindToContext(const Rc<DxvkContext>& ctx) {
    ctx->setDepthStencilState(m_state);
  }

  HRESULT D3D11DepthStencilState::NormalizeDesc(D3D11_DEPTH_STENCIL_DESC* pDesc) {
    if (pDesc->DepthEnable) {
      pDesc->DepthEnable = TRUE;

      if (!ValidateDepthFunc(pDesc->DepthFunc))
        return E_INVALIDARG;
    } else {
      pDesc->DepthFunc      = D3D11_COMPARISON_LESS;
      pDesc->DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
    }

    if (!ValidateDepthWriteMask(pDesc->DepthWriteMask))
      return E_INVALIDARG;

    if (pDesc->StencilEnable) {
      pDesc->StencilEnable = TRUE;

      if (!ValidateStencilFunc(pDesc->FrontFace.StencilFunc)
       || !ValidateStencilOp(pDesc->FrontFace.StencilFailOp)
       || !ValidateStencilOp(pDesc->FrontFace.StencilDepthFailOp)
       || !ValidateStencilOp(pDesc->FrontFace.StencilPassOp))
        return E_INVALIDARG;

      if (!ValidateStencilFunc(pDesc->BackFace.StencilFunc)
       || !ValidateStencilOp(pDesc->BackFace.StencilFailOp)
       || !ValidateStencilOp(pDesc->BackFace.StencilDepthFailOp)
       || !ValidateStencilOp(pDesc->BackFace.StencilPassOp))
        return E_INVALIDARG;
    } else {
      D3D11_DEPTH_STENCILOP_DESC stencilOp;
      stencilOp.StencilFailOp = D3D11_STENCIL_OP_KEEP;
      stencilOp.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
      stencilOp.StencilPassOp = D3D11_STENCIL_OP_KEEP;
      stencilOp.StencilFunc   = D3D11_COMPARISON_ALWAYS;

      pDesc->FrontFace        = stencilOp;
      pDesc->BackFace         = stencilOp;
      pDesc->StencilReadMask  = D3D11_DEFAULT_STENCIL_READ_MASK;
      pDesc->StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
    }

    return S_OK;
  }

  VkStencilOpState D3D11DepthStencilState::DecodeStencilOpState(
    const D3D11_DEPTH_STENCILOP_DESC& StencilDesc,
    const D3D11_DEPTH_STENCIL_DESC&   Desc) const {
    VkStencilOpState result;
    result.failOp      = VK_STENCIL_OP_KEEP;
    result.passOp      = VK_STENCIL_OP_KEEP;
    result.depthFailOp = VK_STENCIL_OP_KEEP;
    result.compareOp   = VK_COMPARE_OP_ALWAYS;
    result.compareMask = Desc.StencilReadMask;
    result.writeMask   = Desc.StencilWriteMask;
    result.reference   = 0;

    if (Desc.StencilEnable) {
      result.failOp      = DecodeStencilOp(StencilDesc.StencilFailOp);
      result.passOp      = DecodeStencilOp(StencilDesc.StencilPassOp);
      result.depthFailOp = DecodeStencilOp(StencilDesc.StencilDepthFailOp);
      result.compareOp   = DecodeCompareOp(StencilDesc.StencilFunc);
    }

    return result;
  }

  VkStencilOp D3D11DepthStencilState::DecodeStencilOp(D3D11_STENCIL_OP Op) const {
    switch (Op) {
      case D3D11_STENCIL_OP_KEEP:       return VK_STENCIL_OP_KEEP;
      case D3D11_STENCIL_OP_ZERO:       return VK_STENCIL_OP_ZERO;
      case D3D11_STENCIL_OP_REPLACE:    return VK_STENCIL_OP_REPLACE;
      case D3D11_STENCIL_OP_INCR_SAT:   return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
      case D3D11_STENCIL_OP_DECR_SAT:   return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
      case D3D11_STENCIL_OP_INVERT:     return VK_STENCIL_OP_INVERT;
      case D3D11_STENCIL_OP_INCR:       return VK_STENCIL_OP_INCREMENT_AND_WRAP;
      case D3D11_STENCIL_OP_DECR:       return VK_STENCIL_OP_DECREMENT_AND_WRAP;
      default:                          return VK_STENCIL_OP_KEEP;
    }
  }

  bool D3D11DepthStencilState::ValidateDepthFunc(D3D11_COMPARISON_FUNC Comparison) {
    return Comparison >= D3D11_COMPARISON_NEVER
        && Comparison <= D3D11_COMPARISON_ALWAYS;
  }

  bool D3D11DepthStencilState::ValidateStencilFunc(D3D11_COMPARISON_FUNC Comparison) {
    return Comparison >= D3D11_COMPARISON_NEVER
        && Comparison <= D3D11_COMPARISON_ALWAYS;
  }

  bool D3D11DepthStencilState::ValidateStencilOp(D3D11_STENCIL_OP StencilOp) {
    return StencilOp >= D3D11_STENCIL_OP_KEEP
        && StencilOp <= D3D11_STENCIL_OP_DECR;
  }

  bool D3D11DepthStencilState::ValidateDepthWriteMask(D3D11_DEPTH_WRITE_MASK Mask) {
    return Mask == D3D11_DEPTH_WRITE_MASK_ZERO
        || Mask == D3D11_DEPTH_WRITE_MASK_ALL;
  }

}


code:
#pragma once

#include "../dxvk/dxvk_device.h"

#include "../d3d10/d3d10_depth_stencil.h"

#include "d3d11_device_child.h"
#include "d3d11_util.h"

namespace dxvk {

  class D3D11Device;

  class D3D11DepthStencilState : public D3D11StateObject<ID3D11DepthStencilState> {

  public:

    using DescType = D3D11_DEPTH_STENCIL_DESC;

    D3D11DepthStencilState(
            D3D11Device*              device,
      const D3D11_DEPTH_STENCIL_DESC& desc);
    ~D3D11DepthStencilState();

    HRESULT STDMETHODCALLTYPE QueryInterface(
            REFIID  riid,
            void**  ppvObject) final;

    void STDMETHODCALLTYPE GetDesc(
            D3D11_DEPTH_STENCIL_DESC* pDesc) final;

    void BindToContext(
      const Rc<DxvkContext>&  ctx);

    D3D10DepthStencilState* GetD3D10Iface() {
      return &m_d3d10;
    }

    static HRESULT NormalizeDesc(
            D3D11_DEPTH_STENCIL_DESC* pDesc);

  private:

    D3D11_DEPTH_STENCIL_DESC  m_desc;
    DxvkDepthStencilState     m_state;
    D3D10DepthStencilState    m_d3d10;

    VkStencilOpState DecodeStencilOpState(
      const D3D11_DEPTH_STENCILOP_DESC& StencilDesc,
      const D3D11_DEPTH_STENCIL_DESC&   Desc) const;

    VkStencilOp DecodeStencilOp(
            D3D11_STENCIL_OP            Op) const;

    static bool ValidateDepthFunc(
            D3D11_COMPARISON_FUNC  Comparison);

    static bool ValidateStencilFunc(
            D3D11_COMPARISON_FUNC  Comparison);

    static bool ValidateStencilOp(
            D3D11_STENCIL_OP       StencilOp);

    static bool ValidateDepthWriteMask(
            D3D11_DEPTH_WRITE_MASK Mask);
  };

}