code:
#include <algorithm>

#include "d3d11_context.h"
#include "d3d11_context_imm.h"
#include "d3d11_video.h"

#include <d3d11_video_blit_frag.h>
#include <d3d11_video_blit_vert.h>

namespace dxvk {

  D3D11VideoProcessorEnumerator::D3D11VideoProcessorEnumerator(
          D3D11Device*            pDevice,
    const D3D11_VIDEO_PROCESSOR_CONTENT_DESC& Desc)
  : D3D11DeviceChild<ID3D11VideoProcessorEnumerator>(pDevice),
    m_desc(Desc) {

  }

  D3D11VideoProcessorEnumerator::~D3D11VideoProcessorEnumerator() {

  }

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

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

  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorContentDesc(
          D3D11_VIDEO_PROCESSOR_CONTENT_DESC* pContentDesc) {
    *pContentDesc = m_desc;
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::CheckVideoProcessorFormat(
          DXGI_FORMAT             Format,
          UINT*                   pFlags) {
    Logger::err("D3D11VideoProcessorEnumerator::CheckVideoProcessorFormat: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorCaps(
          D3D11_VIDEO_PROCESSOR_CAPS* pCaps) {
    Logger::err("D3D11VideoProcessorEnumerator::GetVideoProcessorCaps: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorRateConversionCaps(
          UINT                    TypeIndex,
          D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS* pCaps) {
    Logger::err("D3D11VideoProcessorEnumerator::GetVideoProcessorRateConversionCaps: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorCustomRate(
          UINT                    TypeIndex,
          UINT                    CustomRateIndex,
          D3D11_VIDEO_PROCESSOR_CUSTOM_RATE* pRate) {
    Logger::err("D3D11VideoProcessorEnumerator::GetVideoProcessorCustomRate: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorEnumerator::GetVideoProcessorFilterRange(
          D3D11_VIDEO_PROCESSOR_FILTER        Filter,
          D3D11_VIDEO_PROCESSOR_FILTER_RANGE* pRange) {
    Logger::err("D3D11VideoProcessorEnumerator::GetVideoProcessorFilterRange: Stub");
    return E_NOTIMPL;
  }

  D3D11VideoProcessor::D3D11VideoProcessor(
          D3D11Device*                    pDevice,
          D3D11VideoProcessorEnumerator*  pEnumerator,
          UINT                            RateConversionIndex)
  : D3D11DeviceChild<ID3D11VideoProcessor>(pDevice),
    m_enumerator(pEnumerator), m_rateConversionIndex(RateConversionIndex) {

  }

  D3D11VideoProcessor::~D3D11VideoProcessor() {

  }

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

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

  void STDMETHODCALLTYPE D3D11VideoProcessor::GetContentDesc(
          D3D11_VIDEO_PROCESSOR_CONTENT_DESC *pDesc) {
    m_enumerator->GetVideoProcessorContentDesc(pDesc);
  }

  void STDMETHODCALLTYPE D3D11VideoProcessor::GetRateConversionCaps(
          D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS *pCaps) {
    m_enumerator->GetVideoProcessorRateConversionCaps(m_rateConversionIndex, pCaps);
  }

  D3D11VideoProcessorInputView::D3D11VideoProcessorInputView(
          D3D11Device*            pDevice,
          ID3D11Resource*         pResource,
    const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC& Desc)
  : D3D11DeviceChild<ID3D11VideoProcessorInputView>(pDevice),
    m_resource(pResource), m_desc(Desc) {
    D3D11_COMMON_RESOURCE_DESC resourceDesc = { };
    GetCommonResourceDesc(pResource, &resourceDesc);

    Rc<DxvkImage> dxvkImage = GetCommonTexture(pResource)->GetImage();

    if (!(dxvkImage->info().usage & VK_IMAGE_USAGE_SAMPLED_BIT)) {
      DxvkImageCreateInfo info = dxvkImage->info();
      info.flags  = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
      info.usage  = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
      info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
      info.access = VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT;
      info.tiling = VK_IMAGE_TILING_OPTIMAL;
      info.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
      info.shared = VK_FALSE;
      dxvkImage = m_copy = pDevice->GetDXVKDevice()->createImage(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
    }

    DXGI_VK_FORMAT_INFO formatInfo = pDevice->LookupFormat(resourceDesc.Format, DXGI_VK_FORMAT_MODE_COLOR);
    DXGI_VK_FORMAT_FAMILY formatFamily = pDevice->LookupFamily(resourceDesc.Format, DXGI_VK_FORMAT_MODE_COLOR);

    VkImageAspectFlags aspectMask = imageFormatInfo(formatInfo.Format)->aspectMask;

    DxvkImageViewCreateInfo viewInfo;
    viewInfo.format  = formatInfo.Format;
    viewInfo.swizzle = formatInfo.Swizzle;
    viewInfo.usage   = VK_IMAGE_USAGE_SAMPLED_BIT;

    switch (m_desc.ViewDimension) {
      case D3D11_VPIV_DIMENSION_TEXTURE2D:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D;
        viewInfo.minLevel   = m_desc.Texture2D.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = 0;
        viewInfo.numLayers  = 1;
        break;

      case D3D11_RTV_DIMENSION_UNKNOWN:
        throw DxvkError("Invalid view dimension");
    }

    m_subresources.aspectMask = aspectMask;
    m_subresources.baseArrayLayer = viewInfo.minLayer;
    m_subresources.layerCount = viewInfo.numLayers;
    m_subresources.mipLevel = viewInfo.minLevel;

    for (uint32_t i = 0; aspectMask && i < m_views.size(); i++) {
      viewInfo.aspect = vk::getNextAspect(aspectMask);

      if (viewInfo.aspect != VK_IMAGE_ASPECT_COLOR_BIT)
        viewInfo.format = formatFamily.Formats[i];

      m_views[i] = pDevice->GetDXVKDevice()->createImageView(dxvkImage, viewInfo);
    }

    m_isYCbCr = IsYCbCrFormat(resourceDesc.Format);
  }

  D3D11VideoProcessorInputView::~D3D11VideoProcessorInputView() {

  }

  bool D3D11VideoProcessorInputView::IsYCbCrFormat(DXGI_FORMAT Format) {
    static const std::array<DXGI_FORMAT, 3> s_formats = {{
      DXGI_FORMAT_NV12,
      DXGI_FORMAT_YUY2,
      DXGI_FORMAT_AYUV,
    }};

    return std::find(s_formats.begin(), s_formats.end(), Format) != s_formats.end();
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorInputView::QueryInterface(
          REFIID                  riid,
          void**                  ppvObject) {
    if (riid == __uuidof(IUnknown)
     || riid == __uuidof(ID3D11DeviceChild)
     || riid == __uuidof(ID3D11View)
     || riid == __uuidof(ID3D11VideoProcessorInputView)) {
      *ppvObject = ref(this);
      return S_OK;
    }

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

  void STDMETHODCALLTYPE D3D11VideoProcessorInputView::GetResource(
          ID3D11Resource**        ppResource) {
    *ppResource = m_resource.ref();
  }

  void STDMETHODCALLTYPE D3D11VideoProcessorInputView::GetDesc(
          D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC* pDesc) {
    *pDesc = m_desc;
  }

  D3D11VideoProcessorOutputView::D3D11VideoProcessorOutputView(
          D3D11Device*            pDevice,
          ID3D11Resource*         pResource,
    const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC& Desc)
  : D3D11DeviceChild<ID3D11VideoProcessorOutputView>(pDevice),
    m_resource(pResource), m_desc(Desc) {
    D3D11_COMMON_RESOURCE_DESC resourceDesc = { };
    GetCommonResourceDesc(pResource, &resourceDesc);

    DXGI_VK_FORMAT_INFO formatInfo = pDevice->LookupFormat(
      resourceDesc.Format, DXGI_VK_FORMAT_MODE_COLOR);

    DxvkImageViewCreateInfo viewInfo;
    viewInfo.format  = formatInfo.Format;
    viewInfo.aspect  = imageFormatInfo(viewInfo.format)->aspectMask;
    viewInfo.swizzle = formatInfo.Swizzle;
    viewInfo.usage   = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;

    switch (m_desc.ViewDimension) {
      case D3D11_VPOV_DIMENSION_TEXTURE2D:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D;
        viewInfo.minLevel   = m_desc.Texture2D.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = 0;
        viewInfo.numLayers  = 1;
        break;

      case D3D11_VPOV_DIMENSION_TEXTURE2DARRAY:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
        viewInfo.minLevel   = m_desc.Texture2DArray.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = m_desc.Texture2DArray.FirstArraySlice;
        viewInfo.numLayers  = m_desc.Texture2DArray.ArraySize;
        break;

      case D3D11_RTV_DIMENSION_UNKNOWN:
        throw DxvkError("Invalid view dimension");
    }

    m_view = pDevice->GetDXVKDevice()->createImageView(
      GetCommonTexture(pResource)->GetImage(), viewInfo);
  }

  D3D11VideoProcessorOutputView::~D3D11VideoProcessorOutputView() {

  }

  HRESULT STDMETHODCALLTYPE D3D11VideoProcessorOutputView::QueryInterface(
          REFIID                  riid,
          void**                  ppvObject) {
    if (riid == __uuidof(IUnknown)
     || riid == __uuidof(ID3D11DeviceChild)
     || riid == __uuidof(ID3D11View)
     || riid == __uuidof(ID3D11VideoProcessorOutputView)) {
      *ppvObject = ref(this);
      return S_OK;
    }

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

  void STDMETHODCALLTYPE D3D11VideoProcessorOutputView::GetResource(
          ID3D11Resource**        ppResource) {
    *ppResource = m_resource.ref();
  }

  void STDMETHODCALLTYPE D3D11VideoProcessorOutputView::GetDesc(
          D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC* pDesc) {
    *pDesc = m_desc;
  }

  D3D11VideoContext::D3D11VideoContext(
          D3D11ImmediateContext*  pContext,
    const Rc<DxvkDevice>&         Device)
  : m_ctx(pContext) {
    SpirvCodeBuffer vsCode(d3d11_video_blit_vert);
    SpirvCodeBuffer fsCode(d3d11_video_blit_frag);

    const std::array<DxvkResourceSlot, 4> fsResourceSlots = {{
      { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC               },
      { 1, VK_DESCRIPTOR_TYPE_SAMPLER                              },
      { 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_IMAGE_VIEW_TYPE_2D },
      { 3, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_IMAGE_VIEW_TYPE_2D },
    }};

    DxvkShaderCreateInfo vsInfo;
    vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
    vsInfo.outputMask = 0x1;
    m_vs = new DxvkShader(vsInfo, std::move(vsCode));

    DxvkShaderCreateInfo fsInfo;
    fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
    fsInfo.resourceSlotCount = fsResourceSlots.size();
    fsInfo.resourceSlots = fsResourceSlots.data();
    fsInfo.inputMask = 0x1;
    fsInfo.outputMask = 0x1;
    m_fs = new DxvkShader(fsInfo, std::move(fsCode));

    DxvkSamplerCreateInfo samplerInfo;
    samplerInfo.magFilter       = VK_FILTER_LINEAR;
    samplerInfo.minFilter       = VK_FILTER_LINEAR;
    samplerInfo.mipmapMode      = VK_SAMPLER_MIPMAP_MODE_NEAREST;
    samplerInfo.mipmapLodBias   = 0.0f;
    samplerInfo.mipmapLodMin    = 0.0f;
    samplerInfo.mipmapLodMax    = 0.0f;
    samplerInfo.useAnisotropy   = VK_FALSE;
    samplerInfo.maxAnisotropy   = 1.0f;
    samplerInfo.addressModeU    = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    samplerInfo.addressModeV    = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    samplerInfo.addressModeW    = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    samplerInfo.compareToDepth  = VK_FALSE;
    samplerInfo.compareOp       = VK_COMPARE_OP_ALWAYS;
    samplerInfo.borderColor     = VkClearColorValue();
    samplerInfo.usePixelCoord   = VK_FALSE;
    samplerInfo.nonSeamless     = VK_FALSE;
    m_sampler = Device->createSampler(samplerInfo);

    DxvkBufferCreateInfo bufferInfo;
    bufferInfo.size = sizeof(UboData);
    bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
    bufferInfo.stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    bufferInfo.access = VK_ACCESS_UNIFORM_READ_BIT;
    m_ubo = Device->createBuffer(bufferInfo, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
  }

  D3D11VideoContext::~D3D11VideoContext() {

  }

  ULONG STDMETHODCALLTYPE D3D11VideoContext::AddRef() {
    return m_ctx->AddRef();
  }

  ULONG STDMETHODCALLTYPE D3D11VideoContext::Release() {
    return m_ctx->Release();
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::QueryInterface(
          REFIID                  riid,
          void**                  ppvObject) {
    return m_ctx->QueryInterface(riid, ppvObject);
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::GetPrivateData(
          REFGUID                 Name,
          UINT*                   pDataSize,
          void*                   pData) {
    return m_ctx->GetPrivateData(Name, pDataSize, pData);
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::SetPrivateData(
          REFGUID                 Name,
          UINT                    DataSize,
    const void*                   pData)  {
    return m_ctx->SetPrivateData(Name, DataSize, pData);
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::SetPrivateDataInterface(
          REFGUID                 Name,
    const IUnknown*               pUnknown) {
    return m_ctx->SetPrivateDataInterface(Name, pUnknown);
  }

  void STDMETHODCALLTYPE D3D11VideoContext::GetDevice(
          ID3D11Device**          ppDevice) {
    return m_ctx->GetDevice(ppDevice);
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::GetDecoderBuffer(
          ID3D11VideoDecoder*             pDecoder,
          D3D11_VIDEO_DECODER_BUFFER_TYPE Type,
          UINT*                           BufferSize,
          void**                          ppBuffer) {
    Logger::err("D3D11VideoContext::GetDecoderBuffer: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::ReleaseDecoderBuffer(
          ID3D11VideoDecoder*             pDecoder,
          D3D11_VIDEO_DECODER_BUFFER_TYPE Type) {
    Logger::err("D3D11VideoContext::ReleaseDecoderBuffer: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::DecoderBeginFrame(
          ID3D11VideoDecoder*             pDecoder,
          ID3D11VideoDecoderOutputView*   pView,
          UINT                            KeySize,
    const void*                           pKey) {
    Logger::err("D3D11VideoContext::DecoderBeginFrame: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::DecoderEndFrame(
          ID3D11VideoDecoder*             pDecoder) {
    Logger::err("D3D11VideoContext::DecoderEndFrame: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::SubmitDecoderBuffers(
          ID3D11VideoDecoder*             pDecoder,
          UINT                            BufferCount,
    const D3D11_VIDEO_DECODER_BUFFER_DESC* pBufferDescs) {
    Logger::err("D3D11VideoContext::SubmitDecoderBuffers: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::DecoderExtension(
          ID3D11VideoDecoder*             pDecoder,
    const D3D11_VIDEO_DECODER_EXTENSION*  pExtension) {
    Logger::err("D3D11VideoContext::DecoderExtension: Stub");
    return E_NOTIMPL;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputTargetRect(
          ID3D11VideoProcessor*           pVideoProcessor,
          BOOL                            Enable,
    const RECT*                           pRect) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();
    state->outputTargetRectEnabled = Enable;

    if (Enable)
      state->outputTargetRect = *pRect;

    static bool errorShown = false;

    if (!std::exchange(errorShown, true))
      Logger::err("D3D11VideoContext::VideoProcessorSetOutputTargetRect: Stub.");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputBackgroundColor(
          ID3D11VideoProcessor*           pVideoProcessor,
          BOOL                            YCbCr,
    const D3D11_VIDEO_COLOR*              pColor) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();
    state->outputBackgroundColorIsYCbCr = YCbCr;
    state->outputBackgroundColor = *pColor;

    static bool errorShown = false;

    if (!std::exchange(errorShown, true))
      Logger::err("D3D11VideoContext::VideoProcessorSetOutputBackgroundColor: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputColorSpace(
          ID3D11VideoProcessor*           pVideoProcessor,
    const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();
    state->outputColorSpace = *pColorSpace;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputAlphaFillMode(
          ID3D11VideoProcessor*           pVideoProcessor,
          D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE AlphaFillMode,
          UINT                            StreamIndex) {
    Logger::err("D3D11VideoContext::VideoProcessorSetOutputAlphaFillMode: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputConstriction(
          ID3D11VideoProcessor*           pVideoProcessor,
          BOOL                            Enable,
          SIZE                            Size) {
    Logger::err("D3D11VideoContext::VideoProcessorSetOutputConstriction: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputStereoMode(
          ID3D11VideoProcessor*           pVideoProcessor,
          BOOL                            Enable) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();
    state->outputStereoModeEnabled = Enable;

    if (Enable)
      Logger::err("D3D11VideoContext: Stereo output not supported");
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetOutputExtension(
          ID3D11VideoProcessor*           pVideoProcessor,
    const GUID*                           pExtensionGuid,
          UINT                            DataSize,
          void*                           pData) {
    Logger::err("D3D11VideoContext::VideoProcessorSetOutputExtension: Stub");
    return E_NOTIMPL;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamFrameFormat(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          D3D11_VIDEO_FRAME_FORMAT        Format) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    state->frameFormat = Format;

    if (Format != D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE)
      Logger::err(str::format("D3D11VideoContext: Unsupported frame format: ", Format));
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamColorSpace(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
    const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    state->colorSpace = *pColorSpace;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamOutputRate(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          D3D11_VIDEO_PROCESSOR_OUTPUT_RATE Rate,
          BOOL                            Repeat,
    const DXGI_RATIONAL*                  CustomRate) {
    Logger::err("D3D11VideoContext::VideoProcessorSetStreamOutputRate: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamSourceRect(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL                            Enable,
    const RECT*                           pRect) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    state->srcRectEnabled = Enable;

    if (Enable)
      state->srcRect = *pRect;

    static bool errorShown = false;

    if (!std::exchange(errorShown, true))
      Logger::err("D3D11VideoContext::VideoProcessorSetStreamSourceRect: Stub.");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamDestRect(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL                            Enable,
    const RECT*                           pRect) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    state->dstRectEnabled = Enable;

    if (Enable)
      state->dstRect = *pRect;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamAlpha(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL                            Enable,
          FLOAT                           Alpha) {
    Logger::err("D3D11VideoContext::VideoProcessorSetStreamAlpha: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamPalette(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          UINT                            EntryCount,
    const UINT*                           pEntries) {
    Logger::err("D3D11VideoContext::VideoProcessorSetStreamPalette: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamPixelAspectRatio(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL                            Enable,
    const DXGI_RATIONAL*                  pSrcAspectRatio,
    const DXGI_RATIONAL*                  pDstAspectRatio) {
    Logger::err("D3D11VideoContext::VideoProcessorSetStreamPixelAspectRatio: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamLumaKey(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL                            Enable,
          FLOAT                           Lower,
          FLOAT                           Upper) {
    Logger::err("D3D11VideoContext::VideoProcessorSetStreamLumaKey: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamStereoFormat(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL                            Enable,
          D3D11_VIDEO_PROCESSOR_STEREO_FORMAT Format,
          BOOL                            LeftViewFrame0,
          BOOL                            BaseViewFrame0,
          D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE FlipMode,
          int                             MonoOffset) {
    Logger::err("D3D11VideoContext::VideoProcessorSetStreamStereoFormat: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamAutoProcessingMode(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL                            Enable) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    state->autoProcessingEnabled = Enable;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamFilter(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          D3D11_VIDEO_PROCESSOR_FILTER    Filter,
          BOOL                            Enable,
          int                             Level) {
    Logger::err("D3D11VideoContext::VideoProcessorSetStreamFilter: Stub");
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamExtension(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
    const GUID*                           pExtensionGuid,
          UINT                            DataSize,
          void*                           pData) {
    Logger::err("D3D11VideoContext::VideoProcessorSetStreamExtension: Stub");
    return E_NOTIMPL;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorSetStreamRotation(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL                            Enable,
          D3D11_VIDEO_PROCESSOR_ROTATION  Rotation) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    state->rotationEnabled = Enable;
    state->rotation = Rotation;

    if (Enable && Rotation != D3D11_VIDEO_PROCESSOR_ROTATION_IDENTITY)
      Logger::err(str::format("D3D11VideoContext: Unsupported rotation: ", Rotation));
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputTargetRect(
          ID3D11VideoProcessor*           pVideoProcessor,
          BOOL*                           pEnabled,
          RECT*                           pRect) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();

    if (pEnabled)
      *pEnabled = state->outputTargetRectEnabled;

    if (pRect)
      *pRect = state->outputTargetRect;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputBackgroundColor(
          ID3D11VideoProcessor*           pVideoProcessor,
          BOOL*                           pYCbCr,
          D3D11_VIDEO_COLOR*              pColor) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();

    if (pYCbCr)
      *pYCbCr = state->outputBackgroundColorIsYCbCr;

    if (pColor)
      *pColor = state->outputBackgroundColor;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputColorSpace(
          ID3D11VideoProcessor*           pVideoProcessor,
          D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();

    if (pColorSpace)
      *pColorSpace = state->outputColorSpace;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputAlphaFillMode(
          ID3D11VideoProcessor*           pVideoProcessor,
          D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE* pAlphaFillMode,
          UINT*                           pStreamIndex) {
    Logger::err("D3D11VideoContext::VideoProcessorGetOutputAlphaFillMode: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputConstriction(
          ID3D11VideoProcessor*           pVideoProcessor,
          BOOL*                           pEnabled,
          SIZE*                           pSize) {
    Logger::err("D3D11VideoContext::VideoProcessorGetOutputConstriction: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputStereoMode(
          ID3D11VideoProcessor*           pVideoProcessor,
          BOOL*                           pEnabled) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetState();

    if (pEnabled)
      *pEnabled = state->outputStereoModeEnabled;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetOutputExtension(
          ID3D11VideoProcessor*           pVideoProcessor,
    const GUID*                           pExtensionGuid,
          UINT                            DataSize,
          void*                           pData) {
    Logger::err("D3D11VideoContext::VideoProcessorGetOutputExtension: Stub");
    return E_NOTIMPL;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamFrameFormat(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          D3D11_VIDEO_FRAME_FORMAT*       pFormat) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    if (pFormat)
      *pFormat = state->frameFormat;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamColorSpace(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    if (pColorSpace)
      *pColorSpace = state->colorSpace;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamOutputRate(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          D3D11_VIDEO_PROCESSOR_OUTPUT_RATE* pRate,
          BOOL*                           pRepeat,
          DXGI_RATIONAL*                  pCustomRate) {
    Logger::err("D3D11VideoContext::VideoProcessorGetStreamOutputRate: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamSourceRect(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL*                           pEnabled,
          RECT*                           pRect) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    if (pEnabled)
      *pEnabled = state->srcRectEnabled;

    if (pRect)
      *pRect = state->srcRect;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamDestRect(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL*                           pEnabled,
          RECT*                           pRect) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    if (pEnabled)
      *pEnabled = state->dstRectEnabled;

    if (pRect)
      *pRect = state->dstRect;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamAlpha(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL*                           pEnabled,
          FLOAT*                          pAlpha) {
    Logger::err("D3D11VideoContext::VideoProcessorGetStreamAlpha: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamPalette(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          UINT                            EntryCount,
          UINT*                           pEntries) {
    Logger::err("D3D11VideoContext::VideoProcessorGetStreamPalette: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamPixelAspectRatio(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL*                           pEnabled,
          DXGI_RATIONAL*                  pSrcAspectRatio,
          DXGI_RATIONAL*                  pDstAspectRatio) {
    Logger::err("D3D11VideoContext::VideoProcessorGetStreamPixelAspectRatio: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamLumaKey(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL*                           pEnabled,
          FLOAT*                          pLower,
          FLOAT*                          pUpper) {
    Logger::err("D3D11VideoContext::VideoProcessorGetStreamLumaKey: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamStereoFormat(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL*                           pEnabled,
          D3D11_VIDEO_PROCESSOR_STEREO_FORMAT* pFormat,
          BOOL*                           pLeftViewFrame0,
          BOOL*                           pBaseViewFrame0,
          D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE* pFlipMode,
          int*                            pMonoOffset) {
    Logger::err("D3D11VideoContext::VideoProcessorGetStreamStereoFormat: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamAutoProcessingMode(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL*                           pEnabled) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    *pEnabled = state->autoProcessingEnabled;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamFilter(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          D3D11_VIDEO_PROCESSOR_FILTER    Filter,
          BOOL*                           pEnabled,
          int*                            pLevel) {
    Logger::err("D3D11VideoContext::VideoProcessorGetStreamFilter: Stub");
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamExtension(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
    const GUID*                           pExtensionGuid,
          UINT                            DataSize,
          void*                           pData) {
    Logger::err("D3D11VideoContext::VideoProcessorGetStreamExtension: Stub");
    return E_NOTIMPL;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorGetStreamRotation(
          ID3D11VideoProcessor*           pVideoProcessor,
          UINT                            StreamIndex,
          BOOL*                           pEnable,
          D3D11_VIDEO_PROCESSOR_ROTATION* pRotation) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto state = static_cast<D3D11VideoProcessor*>(pVideoProcessor)->GetStreamState(StreamIndex);

    if (!state)
      return;

    if (pEnable)
      *pEnable = state->rotationEnabled;

    if (pRotation)
      *pRotation = state->rotation;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::VideoProcessorBlt(
          ID3D11VideoProcessor*           pVideoProcessor,
          ID3D11VideoProcessorOutputView* pOutputView,
          UINT                            FrameIdx,
          UINT                            StreamCount,
    const D3D11_VIDEO_PROCESSOR_STREAM*   pStreams) {
    D3D10DeviceLock lock = m_ctx->LockContext();

    auto videoProcessor = static_cast<D3D11VideoProcessor*>(pVideoProcessor);
    bool hasStreamsEnabled = false;

    // Resetting and restoring all context state incurs
    // a lot of overhead, so only do it as necessary
    for (uint32_t i = 0; i < StreamCount; i++) {
      auto streamState = videoProcessor->GetStreamState(i);

      if (!pStreams[i].Enable || !streamState)
        continue;

      if (!hasStreamsEnabled) {
        m_ctx->ResetState();
        BindOutputView(pOutputView);
        hasStreamsEnabled = true;
      }

      BlitStream(streamState, &pStreams[i]);
    }

    if (hasStreamsEnabled)
      m_ctx->RestoreState();

    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::NegotiateCryptoSessionKeyExchange(
          ID3D11CryptoSession*            pSession,
          UINT                            DataSize,
          void*                           pData) {
    Logger::err("D3D11VideoContext::NegotiateCryptoSessionKeyExchange: Stub");
    return E_NOTIMPL;
  }

  void STDMETHODCALLTYPE D3D11VideoContext::EncryptionBlt(
          ID3D11CryptoSession*            pSession,
          ID3D11Texture2D*                pSrcSurface,
          ID3D11Texture2D*                pDstSurface,
          UINT                            IVSize,
          void*                           pIV) {
    Logger::err("D3D11VideoContext::EncryptionBlt: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::DecryptionBlt(
          ID3D11CryptoSession*            pSession,
          ID3D11Texture2D*                pSrcSurface,
          ID3D11Texture2D*                pDstSurface,
          D3D11_ENCRYPTED_BLOCK_INFO*     pBlockInfo,
          UINT                            KeySize,
    const void*                           pKey,
          UINT                            IVSize,
          void*                           pIV) {
    Logger::err("D3D11VideoContext::DecryptionBlt: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::StartSessionKeyRefresh(
          ID3D11CryptoSession*            pSession,
          UINT                            RandomNumberSize,
          void*                           pRandomNumber) {
    Logger::err("D3D11VideoContext::StartSessionKeyRefresh: Stub");
  }

  void STDMETHODCALLTYPE D3D11VideoContext::FinishSessionKeyRefresh(
          ID3D11CryptoSession*            pSession) {
    Logger::err("D3D11VideoContext::FinishSessionKeyRefresh: Stub");
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::GetEncryptionBltKey(
          ID3D11CryptoSession*            pSession,
          UINT                            KeySize,
          void*                           pKey) {
    Logger::err("D3D11VideoContext::GetEncryptionBltKey: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::NegotiateAuthenticatedChannelKeyExchange(
          ID3D11AuthenticatedChannel*     pChannel,
          UINT                            DataSize,
          void*                           pData) {
    Logger::err("D3D11VideoContext::NegotiateAuthenticatedChannelKeyExchange: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::QueryAuthenticatedChannel(
          ID3D11AuthenticatedChannel*     pChannel,
          UINT                            InputSize,
    const void*                           pInput,
          UINT                            OutputSize,
          void*                           pOutput) {
    Logger::err("D3D11VideoContext::QueryAuthenticatedChannel: Stub");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE D3D11VideoContext::ConfigureAuthenticatedChannel(
          ID3D11AuthenticatedChannel*     pChannel,
          UINT                            InputSize,
    const void*                           pInput,
          D3D11_AUTHENTICATED_CONFIGURE_OUTPUT* pOutput) {
    Logger::err("D3D11VideoContext::ConfigureAuthenticatedChannel: Stub");
    return E_NOTIMPL;
  }

  void D3D11VideoContext::ApplyColorMatrix(float pDst[3][4], const float pSrc[3][4]) {
    float result[3][4];

    for (uint32_t i = 0; i < 3; i++) {
      for (uint32_t j = 0; j < 4; j++) {
        result[i][j] = pSrc[i][0] * pDst[0][j]
                     + pSrc[i][1] * pDst[1][j]
                     + pSrc[i][2] * pDst[2][j]
                     + pSrc[i][3] * float(j == 3);
      }
    }

    memcpy(pDst, &result[0][0], sizeof(result));
  }

  void D3D11VideoContext::ApplyYCbCrMatrix(float pColorMatrix[3][4], bool UseBt709) {
    static const float pretransform[3][4] = {
      { 0.0f, 1.0f, 0.0f,  0.0f },
      { 0.0f, 0.0f, 1.0f, -0.5f },
      { 1.0f, 0.0f, 0.0f, -0.5f },
    };

    static const float bt601[3][4] = {
      { 1.0f,  0.000000f,  1.402000f, 0.0f },
      { 1.0f, -0.344136f, -0.714136f, 0.0f },
      { 1.0f,  1.772000f,  0.000000f, 0.0f },
    };

    static const float bt709[3][4] = {
      { 1.0f,  0.000000f,  1.574800f, 0.0f },
      { 1.0f, -0.187324f, -0.468124f, 0.0f },
      { 1.0f,  1.855600f,  0.000000f, 0.0f },
    };

    ApplyColorMatrix(pColorMatrix, pretransform);
    ApplyColorMatrix(pColorMatrix, UseBt709 ? bt709 : bt601);
  }

  void D3D11VideoContext::BindOutputView(
          ID3D11VideoProcessorOutputView* pOutputView) {
    auto dxvkView = static_cast<D3D11VideoProcessorOutputView*>(pOutputView)->GetView();

    m_ctx->EmitCs([this, cView = dxvkView] (DxvkContext* ctx) {
      DxvkRenderTargets rt;
      rt.color[0].view = cView;
      rt.color[0].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

      ctx->bindRenderTargets(rt);
      ctx->bindShader(VK_SHADER_STAGE_VERTEX_BIT, m_vs);
      ctx->bindShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_fs);
      ctx->bindResourceBuffer(0, DxvkBufferSlice(m_ubo));

      DxvkInputAssemblyState iaState;
      iaState.primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
      iaState.primitiveRestart = VK_FALSE;
      iaState.patchVertexCount = 0;
      ctx->setInputAssemblyState(iaState);
    });

    VkExtent3D viewExtent = dxvkView->mipLevelExtent(0);
    m_dstExtent = { viewExtent.width, viewExtent.height };
  }

  void D3D11VideoContext::BlitStream(
    const D3D11VideoProcessorStreamState* pStreamState,
    const D3D11_VIDEO_PROCESSOR_STREAM*   pStream) {
    if (pStream->PastFrames || pStream->FutureFrames)
      Logger::err("D3D11VideoContext: Ignoring non-zero PastFrames and FutureFrames");

    if (pStream->OutputIndex)
      Logger::err("D3D11VideoContext: Ignoring non-zero OutputIndex");

    if (pStream->InputFrameOrField)
      Logger::err("D3D11VideoContext: Ignoring non-zero InputFrameOrField");

    auto view = static_cast<D3D11VideoProcessorInputView*>(pStream->pInputSurface);

    if (view->NeedsCopy()) {
      m_ctx->EmitCs([
        cDstImage     = view->GetShadowCopy(),
        cSrcImage     = view->GetImage(),
        cSrcLayers    = view->GetImageSubresources()
      ] (DxvkContext* ctx) {
        VkImageSubresourceLayers cDstLayers;
        cDstLayers.aspectMask = cSrcLayers.aspectMask;
        cDstLayers.baseArrayLayer = 0;
        cDstLayers.layerCount = cSrcLayers.layerCount;
        cDstLayers.mipLevel = cSrcLayers.mipLevel;

        ctx->copyImage(
          cDstImage, cDstLayers, VkOffset3D(),
          cSrcImage, cSrcLayers, VkOffset3D(),
          cDstImage->info().extent);
      });
    }

    m_ctx->EmitCs([this,
      cStreamState  = *pStreamState,
      cViews        = view->GetViews(),
      cIsYCbCr      = view->IsYCbCr()
    ] (DxvkContext* ctx) {
      VkViewport viewport;
      viewport.x        = 0.0f;
      viewport.y        = 0.0f;
      viewport.width    = float(m_dstExtent.width);
      viewport.height   = float(m_dstExtent.height);
      viewport.minDepth = 0.0f;
      viewport.maxDepth = 1.0f;

      VkRect2D scissor;
      scissor.offset = { 0, 0 };
      scissor.extent = m_dstExtent;

      if (cStreamState.dstRectEnabled) {
        viewport.x      = float(cStreamState.dstRect.left);
        viewport.y      = float(cStreamState.dstRect.top);
        viewport.width  = float(cStreamState.dstRect.right) - viewport.x;
        viewport.height = float(cStreamState.dstRect.bottom) - viewport.y;
      }

      UboData uboData = { };
      uboData.colorMatrix[0][0] = 1.0f;
      uboData.colorMatrix[1][1] = 1.0f;
      uboData.colorMatrix[2][2] = 1.0f;
      uboData.coordMatrix[0][0] = 1.0f;
      uboData.coordMatrix[1][1] = 1.0f;
      uboData.yMin = 0.0f;
      uboData.yMax = 1.0f;

      if (cIsYCbCr)
        ApplyYCbCrMatrix(uboData.colorMatrix, cStreamState.colorSpace.YCbCr_Matrix);

      if (cStreamState.colorSpace.Nominal_Range) {
        uboData.yMin = 0.0627451f;
        uboData.yMax = 0.9215686f;
      }

      DxvkBufferSliceHandle uboSlice = m_ubo->allocSlice();
      memcpy(uboSlice.mapPtr, &uboData, sizeof(uboData));

      ctx->invalidateBuffer(m_ubo, uboSlice);
      ctx->setViewports(1, &viewport, &scissor);
      ctx->bindResourceSampler(1, m_sampler);

      for (uint32_t i = 0; i < cViews.size(); i++)
        ctx->bindResourceView(2 + i, cViews[i], nullptr);

      ctx->draw(3, 1, 0, 0);
    });
  }

}


code:
#pragma once

#include "d3d11_device.h"

namespace dxvk {

  static constexpr uint32_t D3D11_VK_VIDEO_STREAM_COUNT = 8;

  class D3D11VideoProcessorEnumerator : public D3D11DeviceChild<ID3D11VideoProcessorEnumerator> {

  public:

    D3D11VideoProcessorEnumerator(
            D3D11Device*            pDevice,
      const D3D11_VIDEO_PROCESSOR_CONTENT_DESC& Desc);

    ~D3D11VideoProcessorEnumerator();

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

    HRESULT STDMETHODCALLTYPE GetVideoProcessorContentDesc(
            D3D11_VIDEO_PROCESSOR_CONTENT_DESC* pContentDesc);

    HRESULT STDMETHODCALLTYPE CheckVideoProcessorFormat(
            DXGI_FORMAT             Format,
            UINT*                   pFlags);

    HRESULT STDMETHODCALLTYPE GetVideoProcessorCaps(
            D3D11_VIDEO_PROCESSOR_CAPS* pCaps);

    HRESULT STDMETHODCALLTYPE GetVideoProcessorRateConversionCaps(
            UINT                    TypeIndex,
            D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS* pCaps);

    HRESULT STDMETHODCALLTYPE GetVideoProcessorCustomRate(
            UINT                    TypeIndex,
            UINT                    CustomRateIndex,
            D3D11_VIDEO_PROCESSOR_CUSTOM_RATE* pRate);

    HRESULT STDMETHODCALLTYPE GetVideoProcessorFilterRange(
            D3D11_VIDEO_PROCESSOR_FILTER        Filter,
            D3D11_VIDEO_PROCESSOR_FILTER_RANGE* pRange);

  private:

    D3D11_VIDEO_PROCESSOR_CONTENT_DESC  m_desc;

  };

  struct D3D11VideoProcessorStreamState {
    BOOL autoProcessingEnabled  = TRUE;
    BOOL dstRectEnabled         = FALSE;
    BOOL srcRectEnabled         = FALSE;
    BOOL rotationEnabled        = FALSE;
    RECT dstRect                = RECT();
    RECT srcRect                = RECT();
    D3D11_VIDEO_FRAME_FORMAT frameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
    D3D11_VIDEO_PROCESSOR_ROTATION rotation = D3D11_VIDEO_PROCESSOR_ROTATION_IDENTITY;
    D3D11_VIDEO_PROCESSOR_COLOR_SPACE colorSpace = D3D11_VIDEO_PROCESSOR_COLOR_SPACE();
  };

  struct D3D11VideoProcessorState {
    BOOL outputStereoModeEnabled                       = FALSE;
    BOOL outputBackgroundColorIsYCbCr                  = FALSE;
    BOOL outputTargetRectEnabled                       = FALSE;
    RECT outputTargetRect                              = RECT();
    D3D11_VIDEO_COLOR outputBackgroundColor            = D3D11_VIDEO_COLOR();
    D3D11_VIDEO_PROCESSOR_COLOR_SPACE outputColorSpace = D3D11_VIDEO_PROCESSOR_COLOR_SPACE();
  };

  class D3D11VideoProcessor : public D3D11DeviceChild<ID3D11VideoProcessor> {

  public:

    D3D11VideoProcessor(
            D3D11Device*                    pDevice,
            D3D11VideoProcessorEnumerator*  pEnumerator,
            UINT                            RateConversionIndex);

    ~D3D11VideoProcessor();

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

    void STDMETHODCALLTYPE GetContentDesc(
            D3D11_VIDEO_PROCESSOR_CONTENT_DESC *pDesc);

    void STDMETHODCALLTYPE GetRateConversionCaps(
            D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS *pCaps);

    D3D11VideoProcessorState* GetState() {
      return &m_state;
    }

    D3D11VideoProcessorStreamState* GetStreamState(UINT StreamIndex) {
      return StreamIndex < D3D11_VK_VIDEO_STREAM_COUNT
        ? &m_streams[StreamIndex]
        : nullptr;
    }

  private:

    D3D11VideoProcessorEnumerator* m_enumerator;
    uint32_t                       m_rateConversionIndex;
    D3D11VideoProcessorState       m_state;
    D3D11VideoProcessorStreamState m_streams[D3D11_VK_VIDEO_STREAM_COUNT];

  };

  class D3D11VideoProcessorInputView : public D3D11DeviceChild<ID3D11VideoProcessorInputView> {

  public:

    D3D11VideoProcessorInputView(
            D3D11Device*            pDevice,
            ID3D11Resource*         pResource,
      const D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC& Desc);

    ~D3D11VideoProcessorInputView();

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

    void STDMETHODCALLTYPE GetResource(
            ID3D11Resource**        ppResource);

    void STDMETHODCALLTYPE GetDesc(
            D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC* pDesc);

    const bool IsYCbCr() const {
      return m_isYCbCr;
    }

    const bool NeedsCopy() const {
      return m_copy != nullptr;
    }

    Rc<DxvkImage> GetImage() const {
      return GetCommonTexture(m_resource.ptr())->GetImage();
    }

    VkImageSubresourceLayers GetImageSubresources() const {
      return m_subresources;
    }

    Rc<DxvkImage> GetShadowCopy() const {
      return m_copy;
    }

    std::array<Rc<DxvkImageView>, 2> GetViews() const {
      return m_views;
    }

  private:

    Com<ID3D11Resource>                   m_resource;
    D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC m_desc;
    VkImageSubresourceLayers              m_subresources;
    Rc<DxvkImage>                         m_copy;
    std::array<Rc<DxvkImageView>, 2>      m_views;
    bool                                  m_isYCbCr = false;

    static bool IsYCbCrFormat(DXGI_FORMAT Format);

  };

  class D3D11VideoProcessorOutputView : public D3D11DeviceChild<ID3D11VideoProcessorOutputView> {

  public:

    D3D11VideoProcessorOutputView(
            D3D11Device*            pDevice,
            ID3D11Resource*         pResource,
      const D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC& Desc);

    ~D3D11VideoProcessorOutputView();

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

    void STDMETHODCALLTYPE GetResource(
            ID3D11Resource**        ppResource);

    void STDMETHODCALLTYPE GetDesc(
            D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC* pDesc);

    Rc<DxvkImageView> GetView() const {
      return m_view;
    }

  private:

    Com<ID3D11Resource>                     m_resource;
    D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC  m_desc;
    Rc<DxvkImageView>                       m_view;

  };

  class D3D11VideoContext : public ID3D11VideoContext {

  public:

    D3D11VideoContext(
            D3D11ImmediateContext*  pContext,
      const Rc<DxvkDevice>&         Device);

    ~D3D11VideoContext();

    ULONG STDMETHODCALLTYPE AddRef();

    ULONG STDMETHODCALLTYPE Release();

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

    HRESULT STDMETHODCALLTYPE GetPrivateData(
            REFGUID                 Name,
            UINT*                   pDataSize,
            void*                   pData);

    HRESULT STDMETHODCALLTYPE SetPrivateData(
            REFGUID                 Name,
            UINT                    DataSize,
      const void*                   pData);

    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
            REFGUID                 Name,
      const IUnknown*               pUnknown);

    void STDMETHODCALLTYPE GetDevice(
            ID3D11Device**          ppDevice);

    HRESULT STDMETHODCALLTYPE GetDecoderBuffer(
            ID3D11VideoDecoder*             pDecoder,
            D3D11_VIDEO_DECODER_BUFFER_TYPE Type,
            UINT*                           BufferSize,
            void**                          ppBuffer);

    HRESULT STDMETHODCALLTYPE ReleaseDecoderBuffer(
            ID3D11VideoDecoder*             pDecoder,
            D3D11_VIDEO_DECODER_BUFFER_TYPE Type);

    HRESULT STDMETHODCALLTYPE DecoderBeginFrame(
            ID3D11VideoDecoder*             pDecoder,
            ID3D11VideoDecoderOutputView*   pView,
            UINT                            KeySize,
      const void*                           pKey);

    HRESULT STDMETHODCALLTYPE DecoderEndFrame(
            ID3D11VideoDecoder*             pDecoder);

    HRESULT STDMETHODCALLTYPE SubmitDecoderBuffers(
            ID3D11VideoDecoder*             pDecoder,
            UINT                            BufferCount,
      const D3D11_VIDEO_DECODER_BUFFER_DESC* pBufferDescs);

    HRESULT STDMETHODCALLTYPE DecoderExtension(
            ID3D11VideoDecoder*             pDecoder,
      const D3D11_VIDEO_DECODER_EXTENSION*  pExtension);

    void STDMETHODCALLTYPE VideoProcessorSetOutputTargetRect(
            ID3D11VideoProcessor*           pVideoProcessor,
            BOOL                            Enable,
      const RECT*                           pRect);

    void STDMETHODCALLTYPE VideoProcessorSetOutputBackgroundColor(
            ID3D11VideoProcessor*           pVideoProcessor,
            BOOL                            YCbCr,
      const D3D11_VIDEO_COLOR*              pColor);

    void STDMETHODCALLTYPE VideoProcessorSetOutputColorSpace(
            ID3D11VideoProcessor*           pVideoProcessor,
      const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace);

    void STDMETHODCALLTYPE VideoProcessorSetOutputAlphaFillMode(
            ID3D11VideoProcessor*           pVideoProcessor,
            D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE AlphaFillMode,
            UINT                            StreamIndex);

    void STDMETHODCALLTYPE VideoProcessorSetOutputConstriction(
            ID3D11VideoProcessor*           pVideoProcessor,
            BOOL                            Enable,
            SIZE                            Size);

    void STDMETHODCALLTYPE VideoProcessorSetOutputStereoMode(
            ID3D11VideoProcessor*           pVideoProcessor,
            BOOL                            Enable);

    HRESULT STDMETHODCALLTYPE VideoProcessorSetOutputExtension(
            ID3D11VideoProcessor*           pVideoProcessor,
      const GUID*                           pExtensionGuid,
            UINT                            DataSize,
            void*                           pData);

    void STDMETHODCALLTYPE VideoProcessorSetStreamFrameFormat(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            D3D11_VIDEO_FRAME_FORMAT        Format);

    void STDMETHODCALLTYPE VideoProcessorSetStreamColorSpace(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
      const D3D11_VIDEO_PROCESSOR_COLOR_SPACE *pColorSpace);

    void STDMETHODCALLTYPE VideoProcessorSetStreamOutputRate(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            D3D11_VIDEO_PROCESSOR_OUTPUT_RATE Rate,
            BOOL                            Repeat,
      const DXGI_RATIONAL*                  CustomRate);

    void STDMETHODCALLTYPE VideoProcessorSetStreamSourceRect(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL                            Enable,
      const RECT*                           pRect);

    void STDMETHODCALLTYPE VideoProcessorSetStreamDestRect(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL                            Enable,
      const RECT*                           pRect);

    void STDMETHODCALLTYPE VideoProcessorSetStreamAlpha(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL                            Enable,
            FLOAT                           Alpha);

    void STDMETHODCALLTYPE VideoProcessorSetStreamPalette(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            UINT                            EntryCount,
      const UINT*                           pEntries);

    void STDMETHODCALLTYPE VideoProcessorSetStreamPixelAspectRatio(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL                            Enable,
      const DXGI_RATIONAL*                  pSrcAspectRatio,
      const DXGI_RATIONAL*                  pDstAspectRatio);

    void STDMETHODCALLTYPE VideoProcessorSetStreamLumaKey(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL                            Enable,
            FLOAT                           Lower,
            FLOAT                           Upper);

    void STDMETHODCALLTYPE VideoProcessorSetStreamStereoFormat(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL                            Enable,
            D3D11_VIDEO_PROCESSOR_STEREO_FORMAT Format,
            BOOL                            LeftViewFrame0,
            BOOL                            BaseViewFrame0,
            D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE FlipMode,
            int                             MonoOffset);

    void STDMETHODCALLTYPE VideoProcessorSetStreamAutoProcessingMode(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL                            Enable);

    void STDMETHODCALLTYPE VideoProcessorSetStreamFilter(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            D3D11_VIDEO_PROCESSOR_FILTER    Filter,
            BOOL                            Enable,
            int                             Level);

    HRESULT STDMETHODCALLTYPE VideoProcessorSetStreamExtension(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
      const GUID*                           pExtensionGuid,
            UINT                            DataSize,
            void*                           pData);

    void STDMETHODCALLTYPE VideoProcessorSetStreamRotation(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL                            Enable,
            D3D11_VIDEO_PROCESSOR_ROTATION  Rotation);

    void STDMETHODCALLTYPE VideoProcessorGetOutputTargetRect(
            ID3D11VideoProcessor*           pVideoProcessor,
            BOOL*                           pEnabled,
            RECT*                           pRect);

    void STDMETHODCALLTYPE VideoProcessorGetOutputBackgroundColor(
            ID3D11VideoProcessor*           pVideoProcessor,
            BOOL*                           pYCbCr,
            D3D11_VIDEO_COLOR*              pColor);

    void STDMETHODCALLTYPE VideoProcessorGetOutputColorSpace(
            ID3D11VideoProcessor*           pVideoProcessor,
            D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace);

    void STDMETHODCALLTYPE VideoProcessorGetOutputAlphaFillMode(
            ID3D11VideoProcessor*           pVideoProcessor,
            D3D11_VIDEO_PROCESSOR_ALPHA_FILL_MODE* pAlphaFillMode,
            UINT*                           pStreamIndex);

    void STDMETHODCALLTYPE VideoProcessorGetOutputConstriction(
            ID3D11VideoProcessor*           pVideoProcessor,
            BOOL*                           pEnabled,
            SIZE*                           pSize);

    void STDMETHODCALLTYPE VideoProcessorGetOutputStereoMode(
            ID3D11VideoProcessor*           pVideoProcessor,
            BOOL*                           pEnabled);

    HRESULT STDMETHODCALLTYPE VideoProcessorGetOutputExtension(
            ID3D11VideoProcessor*           pVideoProcessor,
      const GUID*                           pExtensionGuid,
            UINT                            DataSize,
            void*                           pData);

    void STDMETHODCALLTYPE VideoProcessorGetStreamFrameFormat(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            D3D11_VIDEO_FRAME_FORMAT*       pFormat);

    void STDMETHODCALLTYPE VideoProcessorGetStreamColorSpace(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            D3D11_VIDEO_PROCESSOR_COLOR_SPACE* pColorSpace);

    void STDMETHODCALLTYPE VideoProcessorGetStreamOutputRate(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            D3D11_VIDEO_PROCESSOR_OUTPUT_RATE* pRate,
            BOOL*                           pRepeat,
            DXGI_RATIONAL*                  pCustomRate);

    void STDMETHODCALLTYPE VideoProcessorGetStreamSourceRect(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL*                           pEnabled,
            RECT*                           pRect);

    void STDMETHODCALLTYPE VideoProcessorGetStreamDestRect(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL*                           pEnabled,
            RECT*                           pRect);

    void STDMETHODCALLTYPE VideoProcessorGetStreamAlpha(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL*                           pEnabled,
            FLOAT*                          pAlpha);

    void STDMETHODCALLTYPE VideoProcessorGetStreamPalette(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            UINT                            EntryCount,
            UINT*                           pEntries);

    void STDMETHODCALLTYPE VideoProcessorGetStreamPixelAspectRatio(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL*                           pEnabled,
            DXGI_RATIONAL*                  pSrcAspectRatio,
            DXGI_RATIONAL*                  pDstAspectRatio);

    void STDMETHODCALLTYPE VideoProcessorGetStreamLumaKey(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL*                           pEnabled,
            FLOAT*                          pLower,
            FLOAT*                          pUpper);

    void STDMETHODCALLTYPE VideoProcessorGetStreamStereoFormat(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL*                           pEnabled,
            D3D11_VIDEO_PROCESSOR_STEREO_FORMAT* pFormat,
            BOOL*                           pLeftViewFrame0,
            BOOL*                           pBaseViewFrame0,
            D3D11_VIDEO_PROCESSOR_STEREO_FLIP_MODE* pFlipMode,
            int*                            pMonoOffset);

    void STDMETHODCALLTYPE VideoProcessorGetStreamAutoProcessingMode(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL*                           pEnabled);

    void STDMETHODCALLTYPE VideoProcessorGetStreamFilter(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            D3D11_VIDEO_PROCESSOR_FILTER    Filter,
            BOOL*                           pEnabled,
            int*                            pLevel);

    HRESULT STDMETHODCALLTYPE VideoProcessorGetStreamExtension(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
      const GUID*                           pExtensionGuid,
            UINT                            DataSize,
            void*                           pData);

    void STDMETHODCALLTYPE VideoProcessorGetStreamRotation(
            ID3D11VideoProcessor*           pVideoProcessor,
            UINT                            StreamIndex,
            BOOL*                           pEnable,
            D3D11_VIDEO_PROCESSOR_ROTATION* pRotation);

    HRESULT STDMETHODCALLTYPE VideoProcessorBlt(
            ID3D11VideoProcessor*           pVideoProcessor,
            ID3D11VideoProcessorOutputView* pOutputView,
            UINT                            FrameIdx,
            UINT                            StreamCount,
      const D3D11_VIDEO_PROCESSOR_STREAM*   pStreams);

    HRESULT STDMETHODCALLTYPE NegotiateCryptoSessionKeyExchange(
            ID3D11CryptoSession*            pSession,
            UINT                            DataSize,
            void*                           pData);

    void STDMETHODCALLTYPE EncryptionBlt(
            ID3D11CryptoSession*            pSession,
            ID3D11Texture2D*                pSrcSurface,
            ID3D11Texture2D*                pDstSurface,
            UINT                            IVSize,
            void*                           pIV);

    void STDMETHODCALLTYPE DecryptionBlt(
            ID3D11CryptoSession*            pSession,
            ID3D11Texture2D*                pSrcSurface,
            ID3D11Texture2D*                pDstSurface,
            D3D11_ENCRYPTED_BLOCK_INFO*     pBlockInfo,
            UINT                            KeySize,
      const void*                           pKey,
            UINT                            IVSize,
            void*                           pIV);

    void STDMETHODCALLTYPE StartSessionKeyRefresh(
            ID3D11CryptoSession*            pSession,
            UINT                            RandomNumberSize,
            void*                           pRandomNumber);

    void STDMETHODCALLTYPE FinishSessionKeyRefresh(
            ID3D11CryptoSession*            pSession);

    HRESULT STDMETHODCALLTYPE GetEncryptionBltKey(
            ID3D11CryptoSession*            pSession,
            UINT                            KeySize,
            void*                           pKey);

    HRESULT STDMETHODCALLTYPE NegotiateAuthenticatedChannelKeyExchange(
            ID3D11AuthenticatedChannel*     pChannel,
            UINT                            DataSize,
            void*                           pData);

    HRESULT STDMETHODCALLTYPE QueryAuthenticatedChannel(
            ID3D11AuthenticatedChannel*     pChannel,
            UINT                            InputSize,
      const void*                           pInput,
            UINT                            OutputSize,
            void*                           pOutput);

    HRESULT STDMETHODCALLTYPE ConfigureAuthenticatedChannel(
            ID3D11AuthenticatedChannel*     pChannel,
            UINT                            InputSize,
      const void*                           pInput,
            D3D11_AUTHENTICATED_CONFIGURE_OUTPUT* pOutput);

  private:

    struct alignas(16) UboData {
      float colorMatrix[3][4];
      float coordMatrix[3][2];
      float yMin, yMax;
    };

    D3D11ImmediateContext* m_ctx;

    Rc<DxvkSampler> m_sampler;
    Rc<DxvkShader> m_vs;
    Rc<DxvkShader> m_fs;
    Rc<DxvkBuffer> m_ubo;

    VkExtent2D m_dstExtent = { 0u, 0u };

    void ApplyColorMatrix(float pDst[3][4], const float pSrc[3][4]);

    void ApplyYCbCrMatrix(float pColorMatrix[3][4], bool UseBt709);

    void BindOutputView(
            ID3D11VideoProcessorOutputView* pOutputView);

    void BlitStream(
      const D3D11VideoProcessorStreamState* pStreamState,
      const D3D11_VIDEO_PROCESSOR_STREAM*   pStream);

  };

}

repo_name: orochi/dxvk


code:
#pragma once

#include "d3d11_include.h"

namespace dxvk {

  /**
   * \brief Buffer view info
   *
   * Stores the byte range covered
   * by a buffer view.
   */
  struct D3D11_VK_BUFFER_VIEW_INFO {
    VkDeviceSize                Offset;
    VkDeviceSize                Length;
  };

  /**
   * \brief Image view info
   *
   * Stores the subresource range
   * covered by an image view.
   */
  struct D3D11_VK_IMAGE_VIEW_INFO {
    VkImageAspectFlags          Aspects;
    uint32_t                    MinLevel;
    uint32_t                    MinLayer;
    uint32_t                    NumLevels;
    uint32_t                    NumLayers;
  };

  /**
   * \brief Common view info
   *
   * Stores a pointer to the resource as
   * well as the type-specific range that
   * is affected by the view.
   */
  struct D3D11_VK_VIEW_INFO {
    ID3D11Resource*             pResource;
    D3D11_RESOURCE_DIMENSION    Dimension;
    UINT                        BindFlags;
    union {
      D3D11_VK_BUFFER_VIEW_INFO Buffer;
      D3D11_VK_IMAGE_VIEW_INFO  Image;
    };
  };

  /**
   * \brief Checks whether two views overlap
   *
   * Overlapping views may conflict in case
   * one or both views are used for writing.
   * \param [in] a First view to check
   * \param [in] b Second view to check
   * \returns \c true if the views overlap
   */
  inline bool CheckViewOverlap(const D3D11_VK_VIEW_INFO& a, const D3D11_VK_VIEW_INFO b) {
    if (likely(a.pResource != b.pResource))
      return false;

    if (a.Dimension == D3D11_RESOURCE_DIMENSION_BUFFER) {
      // Just check whether the buffer ranges overlap
      return (a.Buffer.Offset < b.Buffer.Offset + b.Buffer.Length)
          && (a.Buffer.Offset + a.Buffer.Length > b.Buffer.Offset);
    } else {
      // Check whether the subresource ranges overlap
      return (a.Image.Aspects & b.Image.Aspects)
          && (a.Image.MinLevel < b.Image.MinLevel + b.Image.NumLevels)
          && (a.Image.MinLayer < b.Image.MinLayer + b.Image.NumLayers)
          && (a.Image.MinLevel + a.Image.NumLevels > b.Image.MinLevel)
          && (a.Image.MinLayer + a.Image.NumLayers > b.Image.MinLayer);
    }
  }

  template<typename T1, typename T2>
  bool CheckViewOverlap(const T1* a, const T2* b) {
    return a && b && CheckViewOverlap(a->GetViewInfo(), b->GetViewInfo());
  }

}


code:
#include "d3d11_device.h"
#include "d3d11_buffer.h"
#include "d3d11_resource.h"
#include "d3d11_texture.h"
#include "d3d11_view_dsv.h"

namespace dxvk {

  D3D11DepthStencilView::D3D11DepthStencilView(
          D3D11Device*                      pDevice,
          ID3D11Resource*                   pResource,
    const D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc)
  : D3D11DeviceChild<ID3D11DepthStencilView>(pDevice),
    m_resource(pResource), m_desc(*pDesc), m_d3d10(this) {
    ResourceAddRefPrivate(m_resource);

    D3D11_COMMON_RESOURCE_DESC resourceDesc;
    GetCommonResourceDesc(pResource, &resourceDesc);

    DxvkImageViewCreateInfo viewInfo;
    viewInfo.format = pDevice->LookupFormat(pDesc->Format, DXGI_VK_FORMAT_MODE_DEPTH).Format;
    viewInfo.aspect = imageFormatInfo(viewInfo.format)->aspectMask;
    viewInfo.usage  = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;

    switch (pDesc->ViewDimension) {
      case D3D11_DSV_DIMENSION_TEXTURE1D:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_1D;
        viewInfo.minLevel   = pDesc->Texture1D.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = 0;
        viewInfo.numLayers  = 1;
        break;

      case D3D11_DSV_DIMENSION_TEXTURE1DARRAY:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
        viewInfo.minLevel   = pDesc->Texture1DArray.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = pDesc->Texture1DArray.FirstArraySlice;
        viewInfo.numLayers  = pDesc->Texture1DArray.ArraySize;
        break;

      case D3D11_DSV_DIMENSION_TEXTURE2D:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D;
        viewInfo.minLevel   = pDesc->Texture2D.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = 0;
        viewInfo.numLayers  = 1;
        break;

      case D3D11_DSV_DIMENSION_TEXTURE2DARRAY:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
        viewInfo.minLevel   = pDesc->Texture2DArray.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = pDesc->Texture2DArray.FirstArraySlice;
        viewInfo.numLayers  = pDesc->Texture2DArray.ArraySize;
        break;

      case D3D11_DSV_DIMENSION_TEXTURE2DMS:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D;
        viewInfo.minLevel   = 0;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = 0;
        viewInfo.numLayers  = 1;
        break;

      case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
        viewInfo.minLevel   = 0;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = pDesc->Texture2DMSArray.FirstArraySlice;
        viewInfo.numLayers  = pDesc->Texture2DMSArray.ArraySize;
        break;

      default:
        throw DxvkError("D3D11: Invalid view dimension for DSV");
    }

    // Normalize view type so that we won't accidentally
    // bind 2D array views and 2D views at the same time
    if (viewInfo.numLayers == 1) {
      if (viewInfo.type == VK_IMAGE_VIEW_TYPE_1D_ARRAY) viewInfo.type = VK_IMAGE_VIEW_TYPE_1D;
      if (viewInfo.type == VK_IMAGE_VIEW_TYPE_2D_ARRAY) viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
    }

    // Populate view info struct
    m_info.pResource = pResource;
    m_info.Dimension = resourceDesc.Dim;
    m_info.BindFlags = resourceDesc.BindFlags;
    m_info.Image.Aspects   = viewInfo.aspect;
    m_info.Image.MinLevel  = viewInfo.minLevel;
    m_info.Image.MinLayer  = viewInfo.minLayer;
    m_info.Image.NumLevels = viewInfo.numLevels;
    m_info.Image.NumLayers = viewInfo.numLayers;

    if (m_desc.Flags & D3D11_DSV_READ_ONLY_DEPTH)
      m_info.Image.Aspects &= ~VK_IMAGE_ASPECT_DEPTH_BIT;

    if (m_desc.Flags & D3D11_DSV_READ_ONLY_STENCIL)
      m_info.Image.Aspects &= ~VK_IMAGE_ASPECT_STENCIL_BIT;

    // Create the underlying image view object
    m_view = pDevice->GetDXVKDevice()->createImageView(
      GetCommonTexture(pResource)->GetImage(), viewInfo);
  }

  D3D11DepthStencilView::~D3D11DepthStencilView() {
    ResourceReleasePrivate(m_resource);
  }

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

    *ppvObject = nullptr;

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

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

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

  void STDMETHODCALLTYPE D3D11DepthStencilView::GetResource(ID3D11Resource** ppResource) {
    *ppResource = ref(m_resource);
  }

  void STDMETHODCALLTYPE D3D11DepthStencilView::GetDesc(D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc) {
    *pDesc = m_desc;
  }

  HRESULT D3D11DepthStencilView::GetDescFromResource(
          ID3D11Resource*                   pResource,
          D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc) {
    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
    pResource->GetType(&resourceDim);
    pDesc->Flags = 0;

    switch (resourceDim) {
      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {
        D3D11_TEXTURE1D_DESC resourceDesc;
        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);

        pDesc->Format = resourceDesc.Format;

        if (resourceDesc.ArraySize == 1) {
          pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE1D;
          pDesc->Texture1D.MipSlice = 0;
        } else {
          pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE1DARRAY;
          pDesc->Texture1DArray.MipSlice        = 0;
          pDesc->Texture1DArray.FirstArraySlice = 0;
          pDesc->Texture1DArray.ArraySize       = resourceDesc.ArraySize;
        }
      } return S_OK;

      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {
        D3D11_TEXTURE2D_DESC resourceDesc;
        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);

        pDesc->Format = resourceDesc.Format;

        if (resourceDesc.SampleDesc.Count == 1) {
          if (resourceDesc.ArraySize == 1) {
            pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
            pDesc->Texture2D.MipSlice = 0;
          } else {
            pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
            pDesc->Texture2DArray.MipSlice        = 0;
            pDesc->Texture2DArray.FirstArraySlice = 0;
            pDesc->Texture2DArray.ArraySize       = resourceDesc.ArraySize;
          }
        } else {
          if (resourceDesc.ArraySize == 1) {
            pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
          } else {
            pDesc->ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY;
            pDesc->Texture2DMSArray.FirstArraySlice = 0;
            pDesc->Texture2DMSArray.ArraySize       = resourceDesc.ArraySize;
          }
        }
      } return S_OK;

      default:
        Logger::err(str::format(
          "D3D11: Unsupported dimension for depth stencil view: ",
          resourceDim));
        return E_INVALIDARG;
    }
  }

  HRESULT D3D11DepthStencilView::NormalizeDesc(
          ID3D11Resource*                   pResource,
          D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc) {
    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
    pResource->GetType(&resourceDim);

    DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
    uint32_t numLayers = 0;

    switch (resourceDim) {
      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {
        D3D11_TEXTURE1D_DESC resourceDesc;
        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);

        if (pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE1D
         && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE1DARRAY) {
          Logger::err("D3D11: Incompatible view dimension for Texture1D");
          return E_INVALIDARG;
        }

        format    = resourceDesc.Format;
        numLayers = resourceDesc.ArraySize;
      } break;

      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {
        D3D11_TEXTURE2D_DESC resourceDesc;
        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);

        if (pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2D
         && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2DARRAY
         && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2DMS
         && pDesc->ViewDimension != D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY) {
          Logger::err("D3D11: Incompatible view dimension for Texture2D");
          return E_INVALIDARG;
        }

        format    = resourceDesc.Format;
        numLayers = resourceDesc.ArraySize;
      } break;

      default:
        return E_INVALIDARG;
    }

    if (pDesc->Format == DXGI_FORMAT_UNKNOWN)
      pDesc->Format = format;

    switch (pDesc->ViewDimension) {
      case D3D11_DSV_DIMENSION_TEXTURE1DARRAY:
        if (pDesc->Texture1DArray.ArraySize > numLayers - pDesc->Texture1DArray.FirstArraySlice)
          pDesc->Texture1DArray.ArraySize = numLayers - pDesc->Texture1DArray.FirstArraySlice;
        break;

      case D3D11_DSV_DIMENSION_TEXTURE2DARRAY:
        if (pDesc->Texture2DArray.ArraySize > numLayers - pDesc->Texture2DArray.FirstArraySlice)
          pDesc->Texture2DArray.ArraySize = numLayers - pDesc->Texture2DArray.FirstArraySlice;
        break;

      case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY:
        if (pDesc->Texture2DMSArray.ArraySize > numLayers - pDesc->Texture2DMSArray.FirstArraySlice)
          pDesc->Texture2DMSArray.ArraySize = numLayers - pDesc->Texture2DMSArray.FirstArraySlice;
        break;

      default:
        break;
    }

    return S_OK;
  }

}


code:
#pragma once

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

#include "../d3d10/d3d10_view_dsv.h"

#include "d3d11_device_child.h"
#include "d3d11_view.h"

namespace dxvk {

  class D3D11Device;

  /**
   * \brief Depth-stencil view
   *
   * Unordered access views are special in that they can
   * have counters, which can be used inside shaders to
   * atomically append or consume structures.
   */
  class D3D11DepthStencilView : public D3D11DeviceChild<ID3D11DepthStencilView> {

  public:

    D3D11DepthStencilView(
            D3D11Device*                      pDevice,
            ID3D11Resource*                   pResource,
      const D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc);

    ~D3D11DepthStencilView();

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

    void STDMETHODCALLTYPE GetResource(ID3D11Resource** ppResource) final;

    void STDMETHODCALLTYPE GetDesc(D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc) final;

    const D3D11_VK_VIEW_INFO& GetViewInfo() const {
      return m_info;
    }

    D3D11_RESOURCE_DIMENSION GetResourceType() const {
      D3D11_RESOURCE_DIMENSION type;
      m_resource->GetType(&type);
      return type;
    }

    Rc<DxvkImageView> GetImageView() const {
      return m_view;
    }

    VkImageLayout GetRenderLayout() const {
      if (m_view->imageInfo().tiling == VK_IMAGE_TILING_OPTIMAL) {
        switch (m_desc.Flags & (D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_READ_ONLY_STENCIL)) {
          default:  // case 0
            return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
          case D3D11_DSV_READ_ONLY_DEPTH:
            return VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR;
          case D3D11_DSV_READ_ONLY_STENCIL:
            return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR;
          case D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_READ_ONLY_STENCIL:
            return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
        }
      } else {
        return VK_IMAGE_LAYOUT_GENERAL;
      }
    }

    VkImageAspectFlags GetWritableAspectMask() const {
      VkImageAspectFlags mask = m_view->formatInfo()->aspectMask;
      if (m_desc.Flags & D3D11_DSV_READ_ONLY_DEPTH)   mask &= ~VK_IMAGE_ASPECT_DEPTH_BIT;
      if (m_desc.Flags & D3D11_DSV_READ_ONLY_STENCIL) mask &= ~VK_IMAGE_ASPECT_STENCIL_BIT;
      return mask;
    }

    D3D10DepthStencilView* GetD3D10Iface() {
      return &m_d3d10;
    }

    static HRESULT GetDescFromResource(
            ID3D11Resource*                   pResource,
            D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc);

    static HRESULT NormalizeDesc(
            ID3D11Resource*                   pResource,
            D3D11_DEPTH_STENCIL_VIEW_DESC*    pDesc);

  private:

    ID3D11Resource*                   m_resource;
    D3D11_DEPTH_STENCIL_VIEW_DESC     m_desc;
    D3D11_VK_VIEW_INFO                m_info;
    Rc<DxvkImageView>                 m_view;
    D3D10DepthStencilView             m_d3d10;

  };

}


code:
#include "d3d11_device.h"
#include "d3d11_buffer.h"
#include "d3d11_resource.h"
#include "d3d11_texture.h"
#include "d3d11_view_rtv.h"

namespace dxvk {

  D3D11RenderTargetView::D3D11RenderTargetView(
          D3D11Device*                      pDevice,
          ID3D11Resource*                   pResource,
    const D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc)
  : D3D11DeviceChild<ID3D11RenderTargetView1>(pDevice),
    m_resource(pResource), m_desc(*pDesc), m_d3d10(this) {
    ResourceAddRefPrivate(m_resource);

    auto texture = GetCommonTexture(pResource);

    D3D11_COMMON_RESOURCE_DESC resourceDesc;
    GetCommonResourceDesc(pResource, &resourceDesc);

    DXGI_VK_FORMAT_INFO formatInfo = pDevice->LookupFormat(
      pDesc->Format, DXGI_VK_FORMAT_MODE_COLOR);

    DxvkImageViewCreateInfo viewInfo;
    viewInfo.format  = formatInfo.Format;
    viewInfo.aspect  = imageFormatInfo(viewInfo.format)->aspectMask;
    viewInfo.swizzle = formatInfo.Swizzle;
    viewInfo.usage   = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;

    switch (pDesc->ViewDimension) {
      case D3D11_RTV_DIMENSION_TEXTURE1D:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_1D;
        viewInfo.minLevel   = pDesc->Texture1D.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = 0;
        viewInfo.numLayers  = 1;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE1DARRAY:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
        viewInfo.minLevel   = pDesc->Texture1DArray.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = pDesc->Texture1DArray.FirstArraySlice;
        viewInfo.numLayers  = pDesc->Texture1DArray.ArraySize;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2D:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D;
        viewInfo.minLevel   = pDesc->Texture2D.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = 0;
        viewInfo.numLayers  = 1;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
        viewInfo.minLevel   = pDesc->Texture2DArray.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = pDesc->Texture2DArray.FirstArraySlice;
        viewInfo.numLayers  = pDesc->Texture2DArray.ArraySize;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2DMS:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D;
        viewInfo.minLevel   = 0;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = 0;
        viewInfo.numLayers  = 1;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
        viewInfo.minLevel   = 0;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = pDesc->Texture2DMSArray.FirstArraySlice;
        viewInfo.numLayers  = pDesc->Texture2DMSArray.ArraySize;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE3D:
        viewInfo.type       = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
        viewInfo.minLevel   = pDesc->Texture3D.MipSlice;
        viewInfo.numLevels  = 1;
        viewInfo.minLayer   = pDesc->Texture3D.FirstWSlice;
        viewInfo.numLayers  = pDesc->Texture3D.WSize;
        break;

      default:
        throw DxvkError("D3D11: Invalid view dimension for RTV");
    }

    if (texture->GetPlaneCount() > 1)
      viewInfo.aspect = vk::getPlaneAspect(GetPlaneSlice(pDesc));

    // Normalize view type so that we won't accidentally
    // bind 2D array views and 2D views at the same time
    if (viewInfo.numLayers == 1) {
      if (viewInfo.type == VK_IMAGE_VIEW_TYPE_1D_ARRAY) viewInfo.type = VK_IMAGE_VIEW_TYPE_1D;
      if (viewInfo.type == VK_IMAGE_VIEW_TYPE_2D_ARRAY) viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
    }

    // Populate view info struct
    m_info.pResource = pResource;
    m_info.Dimension = resourceDesc.Dim;
    m_info.BindFlags = resourceDesc.BindFlags;
    m_info.Image.Aspects   = viewInfo.aspect;
    m_info.Image.MinLevel  = viewInfo.minLevel;
    m_info.Image.MinLayer  = viewInfo.minLayer;
    m_info.Image.NumLevels = viewInfo.numLevels;
    m_info.Image.NumLayers = viewInfo.numLayers;

    // Create the underlying image view object
    m_view = pDevice->GetDXVKDevice()->createImageView(texture->GetImage(), viewInfo);
  }

  D3D11RenderTargetView::~D3D11RenderTargetView() {
    ResourceReleasePrivate(m_resource);
  }

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

    *ppvObject = nullptr;

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

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

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

  void STDMETHODCALLTYPE D3D11RenderTargetView::GetResource(ID3D11Resource** ppResource) {
    *ppResource = ref(m_resource);
  }

  void STDMETHODCALLTYPE D3D11RenderTargetView::GetDesc(D3D11_RENDER_TARGET_VIEW_DESC* pDesc) {
    pDesc->Format            = m_desc.Format;
    pDesc->ViewDimension     = m_desc.ViewDimension;

    switch (m_desc.ViewDimension) {
      case D3D11_RTV_DIMENSION_UNKNOWN:
        break;

      case D3D11_RTV_DIMENSION_BUFFER:
        pDesc->Buffer = m_desc.Buffer;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE1D:
        pDesc->Texture1D = m_desc.Texture1D;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE1DARRAY:
        pDesc->Texture1DArray = m_desc.Texture1DArray;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2D:
        pDesc->Texture2D.MipSlice = m_desc.Texture2D.MipSlice;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:
        pDesc->Texture2DArray.MipSlice        = m_desc.Texture2DArray.MipSlice;
        pDesc->Texture2DArray.FirstArraySlice = m_desc.Texture2DArray.FirstArraySlice;
        pDesc->Texture2DArray.ArraySize       = m_desc.Texture2DArray.ArraySize;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2DMS:
        pDesc->Texture2DMS = m_desc.Texture2DMS;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY:
        pDesc->Texture2DMSArray = m_desc.Texture2DMSArray;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE3D:
        pDesc->Texture3D = m_desc.Texture3D;
        break;
    }
  }

  void STDMETHODCALLTYPE D3D11RenderTargetView::GetDesc1(D3D11_RENDER_TARGET_VIEW_DESC1* pDesc) {
    *pDesc = m_desc;
  }

  HRESULT D3D11RenderTargetView::GetDescFromResource(
          ID3D11Resource*                   pResource,
          D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc) {
    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
    pResource->GetType(&resourceDim);

    switch (resourceDim) {
      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {
        D3D11_TEXTURE1D_DESC resourceDesc;
        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);

        pDesc->Format = resourceDesc.Format;

        if (resourceDesc.ArraySize == 1) {
          pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D;
          pDesc->Texture1D.MipSlice = 0;
        } else {
          pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1DARRAY;
          pDesc->Texture1DArray.MipSlice        = 0;
          pDesc->Texture1DArray.FirstArraySlice = 0;
          pDesc->Texture1DArray.ArraySize       = resourceDesc.ArraySize;
        }
      } return S_OK;

      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {
        D3D11_TEXTURE2D_DESC resourceDesc;
        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);

        pDesc->Format = resourceDesc.Format;

        if (resourceDesc.SampleDesc.Count == 1) {
          if (resourceDesc.ArraySize == 1) {
            pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
            pDesc->Texture2D.MipSlice   = 0;
            pDesc->Texture2D.PlaneSlice = 0;
          } else {
            pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
            pDesc->Texture2DArray.MipSlice        = 0;
            pDesc->Texture2DArray.FirstArraySlice = 0;
            pDesc->Texture2DArray.ArraySize       = resourceDesc.ArraySize;
            pDesc->Texture2DArray.PlaneSlice      = 0;
          }
        } else {
          if (resourceDesc.ArraySize == 1) {
            pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
          } else {
            pDesc->ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
            pDesc->Texture2DMSArray.FirstArraySlice = 0;
            pDesc->Texture2DMSArray.ArraySize       = resourceDesc.ArraySize;
          }
        }
      } return S_OK;

      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: {
        D3D11_TEXTURE3D_DESC resourceDesc;
        static_cast<D3D11Texture3D*>(pResource)->GetDesc(&resourceDesc);

        pDesc->Format         = resourceDesc.Format;
        pDesc->ViewDimension  = D3D11_RTV_DIMENSION_TEXTURE3D;
        pDesc->Texture3D.MipSlice    = 0;
        pDesc->Texture3D.FirstWSlice = 0;
        pDesc->Texture3D.WSize       = resourceDesc.Depth;
      } return S_OK;

      default:
        Logger::err(str::format(
          "D3D11: Unsupported dimension for render target view: ",
          resourceDim));
        return E_INVALIDARG;
    }
  }

  D3D11_RENDER_TARGET_VIEW_DESC1 D3D11RenderTargetView::PromoteDesc(
    const D3D11_RENDER_TARGET_VIEW_DESC*    pDesc,
          UINT                              Plane) {
    D3D11_RENDER_TARGET_VIEW_DESC1 dstDesc;
    dstDesc.Format            = pDesc->Format;
    dstDesc.ViewDimension     = pDesc->ViewDimension;

    switch (pDesc->ViewDimension) {
      case D3D11_RTV_DIMENSION_UNKNOWN:
        break;

      case D3D11_RTV_DIMENSION_BUFFER:
        dstDesc.Buffer = pDesc->Buffer;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE1D:
        dstDesc.Texture1D = pDesc->Texture1D;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE1DARRAY:
        dstDesc.Texture1DArray = pDesc->Texture1DArray;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2D:
        dstDesc.Texture2D.MipSlice   = pDesc->Texture2D.MipSlice;
        dstDesc.Texture2D.PlaneSlice = Plane;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:
        dstDesc.Texture2DArray.MipSlice        = pDesc->Texture2DArray.MipSlice;
        dstDesc.Texture2DArray.FirstArraySlice = pDesc->Texture2DArray.FirstArraySlice;
        dstDesc.Texture2DArray.ArraySize       = pDesc->Texture2DArray.ArraySize;
        dstDesc.Texture2DArray.PlaneSlice      = Plane;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2DMS:
        dstDesc.Texture2DMS = pDesc->Texture2DMS;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY:
        dstDesc.Texture2DMSArray = pDesc->Texture2DMSArray;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE3D:
        dstDesc.Texture3D = pDesc->Texture3D;
        break;
    }

    return dstDesc;
  }

  HRESULT D3D11RenderTargetView::NormalizeDesc(
          ID3D11Resource*                   pResource,
          D3D11_RENDER_TARGET_VIEW_DESC1*   pDesc) {
    D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
    pResource->GetType(&resourceDim);

    DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
    uint32_t numLayers = 0;

    switch (resourceDim) {
      case D3D11_RESOURCE_DIMENSION_BUFFER: {
        if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_BUFFER) {
          Logger::err("D3D11: Incompatible view dimension for Buffer");
          return E_INVALIDARG;
        }
      } break;

      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: {
        D3D11_TEXTURE1D_DESC resourceDesc;
        static_cast<D3D11Texture1D*>(pResource)->GetDesc(&resourceDesc);

        if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE1D
         && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE1DARRAY) {
          Logger::err("D3D11: Incompatible view dimension for Texture1D");
          return E_INVALIDARG;
        }

        format    = resourceDesc.Format;
        numLayers = resourceDesc.ArraySize;
      } break;

      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: {
        D3D11_TEXTURE2D_DESC resourceDesc;
        static_cast<D3D11Texture2D*>(pResource)->GetDesc(&resourceDesc);

        if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2D
         && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2DARRAY
         && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2DMS
         && pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY) {
          Logger::err("D3D11: Incompatible view dimension for Texture2D");
          return E_INVALIDARG;
        }

        format    = resourceDesc.Format;
        numLayers = resourceDesc.ArraySize;
      } break;

      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: {
        D3D11_TEXTURE3D_DESC resourceDesc;
        static_cast<D3D11Texture3D*>(pResource)->GetDesc(&resourceDesc);

        if (pDesc->ViewDimension != D3D11_RTV_DIMENSION_TEXTURE3D) {
          Logger::err("D3D11: Incompatible view dimension for Texture3D");
          return E_INVALIDARG;
        }

        format    = resourceDesc.Format;
        numLayers = std::max(resourceDesc.Depth >> pDesc->Texture3D.MipSlice, 1u);
      } break;

      default:
        return E_INVALIDARG;
    }

    if (pDesc->Format == DXGI_FORMAT_UNKNOWN)
      pDesc->Format = format;

    switch (pDesc->ViewDimension) {
      case D3D11_RTV_DIMENSION_TEXTURE1DARRAY:
        if (pDesc->Texture1DArray.ArraySize > numLayers - pDesc->Texture1DArray.FirstArraySlice)
          pDesc->Texture1DArray.ArraySize = numLayers - pDesc->Texture1DArray.FirstArraySlice;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2D:
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:
        if (pDesc->Texture2DArray.ArraySize > numLayers - pDesc->Texture2DArray.FirstArraySlice)
          pDesc->Texture2DArray.ArraySize = numLayers - pDesc->Texture2DArray.FirstArraySlice;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY:
        if (pDesc->Texture2DMSArray.ArraySize > numLayers - pDesc->Texture2DMSArray.FirstArraySlice)
          pDesc->Texture2DMSArray.ArraySize = numLayers - pDesc->Texture2DMSArray.FirstArraySlice;
        break;

      case D3D11_RTV_DIMENSION_TEXTURE3D:
        if (pDesc->Texture3D.WSize > numLayers - pDesc->Texture3D.FirstWSlice)
          pDesc->Texture3D.WSize = numLayers - pDesc->Texture3D.FirstWSlice;
        break;

      default:
        break;
    }

    return S_OK;
  }

  UINT D3D11RenderTargetView::GetPlaneSlice(const D3D11_RENDER_TARGET_VIEW_DESC1* pDesc) {
    switch (pDesc->ViewDimension) {
      case D3D11_RTV_DIMENSION_TEXTURE2D:
        return pDesc->Texture2D.PlaneSlice;
      case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:
        return pDesc->Texture2DArray.PlaneSlice;
      default:
        return 0;
    }
  }

}