code:
#include <d3dcompiler.h>
#include <d3d11_1.h>
#include <dxgi1_3.h>

#include <windows.h>
#include <windowsx.h>

#include <cstring>
#include <string>
#include <sstream>

#include "../test_utils.h"

using namespace dxvk;

struct Vertex {
  float x, y;
};

struct VsConstants {
  float x, y;
  float w, h;
};

struct VsConstantsPad {
  VsConstants data;
  uint32_t pad[60];
};

struct PsConstants {
  float r, g, b, a;
};

struct DrawOptions {
  bool mapDiscardOnce;
  bool sortByTexture;
  bool drawIndexed;
};

const std::string g_vertexShaderCode =
  "cbuffer vs_cb : register(b0) {\n"
  "  float2 v_offset;\n"
  "  float2 v_scale;\n"
  "};\n"
  "float4 main(float4 v_pos : IN_POSITION) : SV_POSITION {\n"
  "  float2 coord = 2.0f * (v_pos * v_scale + v_offset) - 1.0f;\n"
  "  return float4(coord, 0.0f, 1.0f);\n"
  "}\n";

const std::string g_pixelShaderCode =
  "Texture2D<float4> tex0 : register(t0);"
  "cbuffer ps_cb : register(b0) {\n"
  "  float4 color;\n"
  "};\n"
  "float4 main() : SV_TARGET {\n"
  "  return color * tex0.Load(int3(0, 0, 0));\n"
  "}\n";

class TriangleApp {

public:

  TriangleApp(HINSTANCE instance, HWND window)
  : m_window(window) {
    Com<ID3D11Device> device;

    D3D_FEATURE_LEVEL fl = D3D_FEATURE_LEVEL_11_1;

    HRESULT status = D3D11CreateDevice(
      nullptr, D3D_DRIVER_TYPE_HARDWARE,
      nullptr, 0, &fl, 1, D3D11_SDK_VERSION,
      &device, nullptr, nullptr);

    if (FAILED(status)) {
      std::cerr << "Failed to create D3D11 device" << std::endl;
      return;
    }

    if (FAILED(device->QueryInterface(IID_PPV_ARGS(&m_device)))) {
      std::cerr << "Failed to query ID3D11DeviceContext1" << std::endl;
      return;
    }

    Com<IDXGIDevice> dxgiDevice;

    if (FAILED(m_device->QueryInterface(IID_PPV_ARGS(&dxgiDevice)))) {
      std::cerr << "Failed to query DXGI device" << std::endl;
      return;
    }

    if (FAILED(dxgiDevice->GetAdapter(&m_adapter))) {
      std::cerr << "Failed to query DXGI adapter" << std::endl;
      return;
    }

    if (FAILED(m_adapter->GetParent(IID_PPV_ARGS(&m_factory)))) {
      std::cerr << "Failed to query DXGI factory" << std::endl;
      return;
    }

    m_device->GetImmediateContext1(&m_context);

    DXGI_SWAP_CHAIN_DESC1 swapDesc;
    swapDesc.Width          = m_windowSizeW;
    swapDesc.Height         = m_windowSizeH;
    swapDesc.Format         = DXGI_FORMAT_R8G8B8A8_UNORM;
    swapDesc.Stereo         = FALSE;
    swapDesc.SampleDesc     = { 1, 0 };
    swapDesc.BufferUsage    = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapDesc.BufferCount    = 3;
    swapDesc.Scaling        = DXGI_SCALING_STRETCH;
    swapDesc.SwapEffect     = DXGI_SWAP_EFFECT_FLIP_DISCARD;
    swapDesc.AlphaMode      = DXGI_ALPHA_MODE_UNSPECIFIED;
    swapDesc.Flags          = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT
                            | DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;

    DXGI_SWAP_CHAIN_FULLSCREEN_DESC fsDesc;
    fsDesc.RefreshRate      = { 0, 0 };
    fsDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    fsDesc.Scaling          = DXGI_MODE_SCALING_UNSPECIFIED;
    fsDesc.Windowed         = TRUE;

    Com<IDXGISwapChain1> swapChain;
    if (FAILED(m_factory->CreateSwapChainForHwnd(m_device.ptr(), m_window, &swapDesc, &fsDesc, nullptr, &swapChain))) {
      std::cerr << "Failed to create DXGI swap chain" << std::endl;
      return;
    }

    if (FAILED(swapChain->QueryInterface(IID_PPV_ARGS(&m_swapChain)))) {
      std::cerr << "Failed to query DXGI swap chain interface" << std::endl;
      return;
    }

    m_factory->MakeWindowAssociation(m_window, 0);

    Com<ID3DBlob> vertexShaderBlob;
    Com<ID3DBlob> pixelShaderBlob;

    if (FAILED(D3DCompile(g_vertexShaderCode.data(), g_vertexShaderCode.size(),
        "Vertex shader", nullptr, nullptr, "main", "vs_5_0", 0, 0, &vertexShaderBlob, nullptr))) {
      std::cerr << "Failed to compile vertex shader" << std::endl;
      return;
    }

    if (FAILED(D3DCompile(g_pixelShaderCode.data(), g_pixelShaderCode.size(),
        "Pixel shader", nullptr, nullptr, "main", "ps_5_0", 0, 0, &pixelShaderBlob, nullptr))) {
      std::cerr << "Failed to compile pixel shader" << std::endl;
      return;
    }

    if (FAILED(m_device->CreateVertexShader(
        vertexShaderBlob->GetBufferPointer(),
        vertexShaderBlob->GetBufferSize(),
        nullptr, &m_vs))) {
      std::cerr << "Failed to create vertex shader" << std::endl;
      return;
    }

    if (FAILED(m_device->CreatePixelShader(
        pixelShaderBlob->GetBufferPointer(),
        pixelShaderBlob->GetBufferSize(),
        nullptr, &m_ps))) {
      std::cerr << "Failed to create pixel shader" << std::endl;
      return;
    }

    std::array<D3D11_INPUT_ELEMENT_DESC, 1> vertexFormatDesc = {{
      { "IN_POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    }};

    if (FAILED(m_device->CreateInputLayout(
        vertexFormatDesc.data(),
        vertexFormatDesc.size(),
        vertexShaderBlob->GetBufferPointer(),
        vertexShaderBlob->GetBufferSize(),
        &m_vertexFormat))) {
      std::cerr << "Failed to create input layout" << std::endl;
      return;
    }

    std::array<Vertex, 6> vertexData = {{
      Vertex { -0.3f, 0.1f },
      Vertex {  0.5f, 0.9f },
      Vertex {  1.3f, 0.1f },
      Vertex { -0.3f, 0.9f },
      Vertex {  1.3f, 0.9f },
      Vertex {  0.5f, 0.1f },
    }};

    D3D11_BUFFER_DESC vboDesc;
    vboDesc.ByteWidth           = sizeof(vertexData);
    vboDesc.Usage               = D3D11_USAGE_IMMUTABLE;
    vboDesc.BindFlags           = D3D11_BIND_VERTEX_BUFFER;
    vboDesc.CPUAccessFlags      = 0;
    vboDesc.MiscFlags           = 0;
    vboDesc.StructureByteStride = 0;

    D3D11_SUBRESOURCE_DATA vboData;
    vboData.pSysMem             = vertexData.data();
    vboData.SysMemPitch         = vboDesc.ByteWidth;
    vboData.SysMemSlicePitch    = vboDesc.ByteWidth;

    if (FAILED(m_device->CreateBuffer(&vboDesc, &vboData, &m_vbo))) {
      std::cerr << "Failed to create index buffer" << std::endl;
      return;
    }

    std::array<uint32_t, 6> indexData = {{ 0, 1, 2, 3, 4, 5 }};

    D3D11_BUFFER_DESC iboDesc;
    iboDesc.ByteWidth           = sizeof(indexData);
    iboDesc.Usage               = D3D11_USAGE_IMMUTABLE;
    iboDesc.BindFlags           = D3D11_BIND_INDEX_BUFFER;
    iboDesc.CPUAccessFlags      = 0;
    iboDesc.MiscFlags           = 0;
    iboDesc.StructureByteStride = 0;

    D3D11_SUBRESOURCE_DATA iboData;
    iboData.pSysMem             = indexData.data();
    iboData.SysMemPitch         = iboDesc.ByteWidth;
    iboData.SysMemSlicePitch    = iboDesc.ByteWidth;

    if (FAILED(m_device->CreateBuffer(&iboDesc, &iboData, &m_ibo))) {
      std::cerr << "Failed to create index buffer" << std::endl;
      return;
    }

    D3D11_BUFFER_DESC cbDesc;
    cbDesc.ByteWidth            = sizeof(PsConstants);
    cbDesc.Usage                = D3D11_USAGE_DYNAMIC;
    cbDesc.BindFlags            = D3D11_BIND_CONSTANT_BUFFER;
    cbDesc.CPUAccessFlags       = D3D11_CPU_ACCESS_WRITE;
    cbDesc.MiscFlags            = 0;
    cbDesc.StructureByteStride  = 0;

    if (FAILED(m_device->CreateBuffer(&cbDesc, nullptr, &m_cbPs))) {
      std::cerr << "Failed to create constant buffer" << std::endl;
      return;
    }

    cbDesc.ByteWidth            = sizeof(VsConstantsPad) * 128 * 8;

    if (FAILED(m_device->CreateBuffer(&cbDesc, nullptr, &m_cbVs))) {
      std::cerr << "Failed to create constant buffer" << std::endl;
      return;
    }

    std::array<uint32_t, 2> colors = { 0xFFFFFFFF, 0xFFC0C0C0 };

    D3D11_SUBRESOURCE_DATA texData;
    texData.pSysMem             = &colors[0];
    texData.SysMemPitch         = sizeof(colors[0]);
    texData.SysMemSlicePitch    = sizeof(colors[0]);

    D3D11_TEXTURE2D_DESC texDesc;
    texDesc.Width               = 1;
    texDesc.Height              = 1;
    texDesc.MipLevels           = 1;
    texDesc.ArraySize           = 1;
    texDesc.Format              = DXGI_FORMAT_R8G8B8A8_UNORM;
    texDesc.SampleDesc          = { 1, 0 };
    texDesc.Usage               = D3D11_USAGE_IMMUTABLE;
    texDesc.BindFlags           = D3D11_BIND_SHADER_RESOURCE;
    texDesc.CPUAccessFlags      = 0;
    texDesc.MiscFlags           = 0;

    if (FAILED(m_device->CreateTexture2D(&texDesc, &texData, &m_tex0))) {
      std::cerr << "Failed to create texture" << std::endl;
      return;
    }

    texData.pSysMem             = &colors[1];

    if (FAILED(m_device->CreateTexture2D(&texDesc, &texData, &m_tex1))) {
      std::cerr << "Failed to create texture" << std::endl;
      return;
    }

    if (FAILED(m_device->CreateShaderResourceView(m_tex0.ptr(), nullptr, &m_srv0))
     || FAILED(m_device->CreateShaderResourceView(m_tex1.ptr(), nullptr, &m_srv1))) {
      std::cerr << "Failed to create SRV" << std::endl;
      return;
    }

    m_initialized = true;
  }

  ~TriangleApp() {
    m_context->ClearState();
  }

  bool run() {
    if (!m_initialized)
      return false;

    if (m_occluded && (m_occluded = isOccluded()))
      return true;

    if (!beginFrame())
      return true;

    std::array<PsConstants, 2> colors = {{
      PsConstants { 0.25f, 0.25f, 0.25f, 1.0f },
      PsConstants { 0.40f, 0.40f, 0.40f, 1.0f },
    }};

    for (uint32_t i = 0; i < 8; i++) {
      DrawOptions options;
      options.sortByTexture = i & 1;
      options.drawIndexed = i & 2;
      options.mapDiscardOnce = i & 4;
      drawLines(colors[i & 1], options, i);
    }

    if (!endFrame())
      return false;

    updateFps();
    return true;
  }

  void drawLines(const PsConstants& psData, const DrawOptions& options, uint32_t baseY) {
    D3D11_MAPPED_SUBRESOURCE sr;

    // Update color for the row
    m_context->PSSetConstantBuffers(0, 1, &m_cbPs);
    m_context->Map(m_cbPs.ptr(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);
    std::memcpy(sr.pData, &psData, sizeof(psData));
    m_context->Unmap(m_cbPs.ptr(), 0);

    baseY *= 8;

    if (options.mapDiscardOnce) {
      uint32_t drawIndex = 0;

      // Discard and map the entire vertex constant buffer
      // once, then bind sub-ranges while emitting draw calls
      m_context->Map(m_cbVs.ptr(), 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);
      auto vsData = reinterpret_cast<VsConstantsPad*>(sr.pData);

      for (uint32_t y = 0; y < 8; y++) {
        for (uint32_t x = 0; x < 128; x++)
          vsData[drawIndex++].data = getVsConstants(x, baseY + y);
      }

      m_context->Unmap(m_cbVs.ptr(), 0);
    }

    if (options.drawIndexed)
      m_context->IASetIndexBuffer(m_ibo.ptr(), DXGI_FORMAT_R32_UINT, 0);

    uint32_t vsStride = sizeof(Vertex);
    uint32_t vsOffset = 0;
    m_context->IASetVertexBuffers(0, 1, &m_vbo, &vsStride, &vsOffset);

    uint32_t maxZ = options.sortByTexture ? 2 : 1;

    for (uint32_t z = 0; z < maxZ; z++) {
      uint32_t drawIndex = z;

      if (options.sortByTexture) {
        ID3D11ShaderResourceView* view = z ? m_srv1.ptr() : m_srv0.ptr();
        m_context->PSSetShaderResources(0, 1, &view);
      }

      for (uint32_t y = 0; y < 8; y++) {
        for (uint32_t x = z; x < 128; x += maxZ) {
          uint32_t triIndex = (x ^ y) & 1;

          if (!options.mapDiscardOnce) {
            D3D11_MAP mapMode = drawIndex ? D3D11_MAP_WRITE_NO_OVERWRITE : D3D11_MAP_WRITE_DISCARD;
            m_context->Map(m_cbVs.ptr(), 0, mapMode, 0, &sr);
            auto vsData = reinterpret_cast<VsConstantsPad*>(sr.pData);
            vsData[drawIndex].data = getVsConstants(x, baseY + y);
            m_context->Unmap(m_cbVs.ptr(), 0);
          }

          uint32_t constantOffset = 16 * drawIndex;
          uint32_t constantCount  = 16;
          m_context->VSSetConstantBuffers1(0, 1, &m_cbVs, &constantOffset, &constantCount);

          if (!options.sortByTexture) {
            ID3D11ShaderResourceView* view = triIndex ? m_srv1.ptr() : m_srv0.ptr();
            m_context->PSSetShaderResources(0, 1, &view);
          }

          // Submit draw call
          uint32_t baseIndex = 3 * triIndex;

          if (options.drawIndexed)
            m_context->DrawIndexed(3, baseIndex, 0);
          else
            m_context->Draw(3, baseIndex);

          drawIndex += maxZ;
        }
      }
    }
  }

  static VsConstants getVsConstants(uint32_t x, uint32_t y) {
    VsConstants result;
    result.x = float(x) / 128.0f;
    result.y = float(y) / 64.0f;
    result.w = 1.0f / 128.0f;
    result.h = 1.0f / 64.0f;
    return result;
  }

  bool beginFrame() {
    // Make sure we can actually render to the window
    RECT windowRect = { 0, 0, 1024, 600 };
    GetClientRect(m_window, &windowRect);

    uint32_t newWindowSizeW = uint32_t(windowRect.right - windowRect.left);
    uint32_t newWindowSizeH = uint32_t(windowRect.bottom - windowRect.top);

    if (m_windowSizeW != newWindowSizeW || m_windowSizeH != newWindowSizeH) {
      m_rtv = nullptr;
      m_context->ClearState();

      DXGI_SWAP_CHAIN_DESC1 desc;
      m_swapChain->GetDesc1(&desc);

      if (FAILED(m_swapChain->ResizeBuffers(desc.BufferCount,
          newWindowSizeW, newWindowSizeH, desc.Format, desc.Flags))) {
        std::cerr << "Failed to resize back buffers" << std::endl;
        return false;
      }

      Com<ID3D11Texture2D> backBuffer;
      if (FAILED(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer)))) {
        std::cerr << "Failed to get swap chain back buffer" << std::endl;
        return false;
      }

      D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
      rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
      rtvDesc.Format        = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
      rtvDesc.Texture2D     = { 0u };

      if (FAILED(m_device->CreateRenderTargetView(backBuffer.ptr(), &rtvDesc, &m_rtv))) {
        std::cerr << "Failed to create render target view" << std::endl;
        return false;
      }

      m_windowSizeW = newWindowSizeW;
      m_windowSizeH = newWindowSizeH;
    }

    // Set up render state
    FLOAT color[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
    m_context->OMSetRenderTargets(1, &m_rtv, nullptr);
    m_context->ClearRenderTargetView(m_rtv.ptr(), color);

    m_context->VSSetShader(m_vs.ptr(), nullptr, 0);
    m_context->PSSetShader(m_ps.ptr(), nullptr, 0);

    m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    m_context->IASetInputLayout(m_vertexFormat.ptr());

    D3D11_VIEWPORT viewport;
    viewport.TopLeftX     = 0.0f;
    viewport.TopLeftY     = 0.0f;
    viewport.Width        = float(m_windowSizeW);
    viewport.Height       = float(m_windowSizeH);
    viewport.MinDepth     = 0.0f;
    viewport.MaxDepth     = 1.0f;
    m_context->RSSetViewports(1, &viewport);
    return true;
  }

  bool endFrame() {
    HRESULT hr = m_swapChain->Present(0, DXGI_PRESENT_TEST);

    if (hr == S_OK)
      hr = m_swapChain->Present(0, 0);

    m_occluded = hr == DXGI_STATUS_OCCLUDED;
    return true;
  }

  void updateFps() {
    if (!m_qpcFrequency.QuadPart)
      QueryPerformanceFrequency(&m_qpcFrequency);

    if (!m_qpcLastUpdate.QuadPart)
      QueryPerformanceCounter(&m_qpcLastUpdate);

    LARGE_INTEGER now;
    QueryPerformanceCounter(&now);

    m_frameCount++;

    if (now.QuadPart - m_qpcLastUpdate.QuadPart < m_qpcFrequency.QuadPart)
      return;

    double seconds = double(now.QuadPart - m_qpcLastUpdate.QuadPart) / double(m_qpcFrequency.QuadPart);
    double fps = double(m_frameCount) / seconds;

    std::wstringstream str;
    str << L"D3D11 triangle (" << fps << L" FPS)";

    SetWindowTextW(m_window, str.str().c_str());

    m_qpcLastUpdate = now;
    m_frameCount = 0;
  }

  bool isOccluded() {
    return m_swapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED;
  }

private:

  HWND                          m_window;
  uint32_t                      m_windowSizeW = 1024;
  uint32_t                      m_windowSizeH = 600;
  bool                          m_initialized = false;
  bool                          m_occluded = false;

  Com<IDXGIFactory3>            m_factory;
  Com<IDXGIAdapter>             m_adapter;
  Com<ID3D11Device1>            m_device;
  Com<ID3D11DeviceContext1>     m_context;
  Com<IDXGISwapChain2>          m_swapChain;

  Com<ID3D11RenderTargetView>   m_rtv;
  Com<ID3D11Buffer>             m_ibo;
  Com<ID3D11Buffer>             m_vbo;
  Com<ID3D11InputLayout>        m_vertexFormat;

  Com<ID3D11Texture2D>          m_tex0;
  Com<ID3D11Texture2D>          m_tex1;
  Com<ID3D11ShaderResourceView> m_srv0;
  Com<ID3D11ShaderResourceView> m_srv1;

  Com<ID3D11Buffer>             m_cbPs;
  Com<ID3D11Buffer>             m_cbVs;

  Com<ID3D11VertexShader>       m_vs;
  Com<ID3D11PixelShader>        m_ps;

  LARGE_INTEGER                 m_qpcLastUpdate = { };
  LARGE_INTEGER                 m_qpcFrequency  = { };

  uint32_t                      m_frameCount = 0;

};

LRESULT CALLBACK WindowProc(HWND hWnd,
                            UINT message,
                            WPARAM wParam,
                            LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
  WNDCLASSEXW wc = { };
  wc.cbSize = sizeof(wc);
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
  wc.hbrBackground = HBRUSH(COLOR_WINDOW);
  wc.lpszClassName = L"WindowClass";
  RegisterClassExW(&wc);

  HWND hWnd = CreateWindowExW(0, L"WindowClass", L"D3D11 triangle",
    WS_OVERLAPPEDWINDOW, 300, 300, 1024, 600,
    nullptr, nullptr, hInstance, nullptr);
  ShowWindow(hWnd, nCmdShow);

  TriangleApp app(hInstance, hWnd);

  MSG msg;

  while (true) {
    if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
      TranslateMessage(&msg);
      DispatchMessageW(&msg);

      if (msg.message == WM_QUIT)
        return msg.wParam;
    } else {
      if (!app.run())
        break;
    }
  }

  return msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  switch (message) {
    case WM_CLOSE:
      PostQuitMessage(0);
      return 0;
  }

  return DefWindowProcW(hWnd, message, wParam, lParam);
}


code:
#include <d3d11_1.h>

#include <windows.h>
#include <windowsx.h>

#include <cmath>
#include <fstream>
#include <vector>

#include "../test_utils.h"

using namespace dxvk;

class VideoApp {

public:

  VideoApp(HINSTANCE instance, HWND window)
  : m_window(window) {
    // Create base D3D11 device and swap chain
    DXGI_SWAP_CHAIN_DESC swapchainDesc = { };
    swapchainDesc.BufferDesc.Width = m_windowSizeX;
    swapchainDesc.BufferDesc.Height = m_windowSizeY;
    swapchainDesc.BufferDesc.RefreshRate = { 0, 0 };
    swapchainDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    swapchainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    swapchainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    swapchainDesc.BufferCount = 2;
    swapchainDesc.SampleDesc = { 1, 0 };
    swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapchainDesc.OutputWindow = m_window;
    swapchainDesc.Windowed = true;
    swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
    swapchainDesc.Flags = 0;

    HRESULT hr = D3D11CreateDeviceAndSwapChain(nullptr,
      D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0,
      D3D11_SDK_VERSION, &swapchainDesc, &m_swapchain,
      &m_device, nullptr, &m_context);

    if (FAILED(hr)) {
      std::cerr << "Failed to initialize D3D11 device and swap chain" << std::endl;
      return;
    }

    if (FAILED(hr = m_device->QueryInterface(IID_PPV_ARGS(&m_vdevice)))) {
      std::cerr << "Failed to query D3D11 video device" << std::endl;
      return;
    }

    if (FAILED(hr = m_context->QueryInterface(IID_PPV_ARGS(&m_vcontext)))) {
      std::cerr << "Failed to query D3D11 video context" << std::endl;
      return;
    }

    if (FAILED(hr = m_swapchain->ResizeTarget(&swapchainDesc.BufferDesc))) {
      std::cerr << "Failed to resize target" << std::endl;
      return;
    }

    if (FAILED(hr = m_swapchain->GetBuffer(0, IID_PPV_ARGS(&m_swapImage)))) {
      std::cerr << "Failed to query swap chain image" << std::endl;
      return;
    }

    if (FAILED(hr = m_device->CreateRenderTargetView(m_swapImage.ptr(), nullptr, &m_swapImageView))) {
      std::cerr << "Failed to create render target view" << std::endl;
      return;
    }

    // Create video processor instance
    D3D11_VIDEO_PROCESSOR_CONTENT_DESC videoEnumDesc = { };
    videoEnumDesc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
    videoEnumDesc.InputFrameRate = { 60, 1 };
    videoEnumDesc.InputWidth = 128;
    videoEnumDesc.InputHeight = 128;
    videoEnumDesc.OutputFrameRate = { 60, 1 };
    videoEnumDesc.OutputWidth = 256;
    videoEnumDesc.OutputHeight = 256;
    videoEnumDesc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;

    if (FAILED(hr = m_vdevice->CreateVideoProcessorEnumerator(&videoEnumDesc, &m_venum))) {
      std::cerr << "Failed to create D3D11 video processor enumerator" << std::endl;
      return;
    }

    if (FAILED(hr = m_vdevice->CreateVideoProcessor(m_venum.ptr(), 0, &m_vprocessor))) {
      std::cerr << "Failed to create D3D11 video processor" << std::endl;
      return;
    }

    // Video output image and view
    D3D11_TEXTURE2D_DESC textureDesc = { };
    textureDesc.Width = 256;
    textureDesc.Height = 256;
    textureDesc.MipLevels = 1;
    textureDesc.ArraySize = 1;
    textureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    textureDesc.SampleDesc = { 1, 0 };
    textureDesc.Usage = D3D11_USAGE_DEFAULT;
    textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET;

    if (FAILED(hr = m_device->CreateTexture2D(&textureDesc, nullptr, &m_videoOutput))) {
      std::cerr << "Failed to create D3D11 video output image" << std::endl;
      return;
    }

    D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = { };
    outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
    outputDesc.Texture2D.MipSlice = 0;

    if (FAILED(hr = m_vdevice->CreateVideoProcessorOutputView(m_videoOutput.ptr(), m_venum.ptr(), &outputDesc, &m_videoOutputView))) {
      std::cerr << "Failed to create D3D11 video output view" << std::endl;
      return;
    }

    if (FAILED(hr = m_device->CreateRenderTargetView(m_videoOutput.ptr(), nullptr, &m_videoOutputRtv))) {
      std::cerr << "Failed to create video render target view" << std::endl;
      return;
    }

    // RGBA input image and view
    textureDesc.Width = 128;
    textureDesc.Height = 128;
    textureDesc.BindFlags = 0;

    size_t pixelCount = textureDesc.Width * textureDesc.Height;

    size_t rowSizeRgba = textureDesc.Width * 4;
    size_t rowSizeNv12 = textureDesc.Width;
    size_t rowSizeYuy2 = textureDesc.Width * 2;
    size_t imageSizeRgba = textureDesc.Height * rowSizeRgba;
    size_t imageSizeNv12 = pixelCount + pixelCount / 2;
    size_t imageSizeYuy2 = textureDesc.Height * rowSizeYuy2;

    std::vector<uint8_t> srcData(pixelCount * 3);
    std::vector<uint8_t> imgDataRgba(imageSizeRgba);
    std::vector<uint8_t> imgDataNv12(imageSizeNv12);
    std::vector<uint8_t> imgDataYuy2(imageSizeYuy2);
    std::ifstream ifile("video_image.raw", std::ios::binary);

    if (!ifile || !ifile.read(reinterpret_cast<char*>(srcData.data()), srcData.size())) {
      std::cerr << "Failed to read image file" << std::endl;
      return;
    }

    for (size_t i = 0; i < pixelCount; i++) {
      imgDataRgba[4 * i + 0] = srcData[3 * i + 0];
      imgDataRgba[4 * i + 1] = srcData[3 * i + 1];
      imgDataRgba[4 * i + 2] = srcData[3 * i + 2];
      imgDataRgba[4 * i + 3] = 0xFF;

      imgDataNv12[i] = y_coeff(&srcData[3 * i], 0.299000f, 0.587000f, 0.114000f);

      imgDataYuy2[2 * i + 0] = y_coeff(&srcData[3 * i], 0.299000f, 0.587000f, 0.114000f);
      imgDataYuy2[2 * i + 1] = i % 2
        ? c_coeff(&srcData[3 * i], -0.168736f, -0.331264f,  0.500000f)
        : c_coeff(&srcData[3 * i],  0.500000f, -0.418688f, -0.081312f);
    }

    for (size_t y = 0; y < textureDesc.Height / 2; y++) {
      for (size_t x = 0; x < textureDesc.Width / 2; x++) {
        size_t p = textureDesc.Width * (2 * y) + 2 * x;
        size_t i = pixelCount + textureDesc.Width * y + 2 * x;
        imgDataNv12[i + 0] = c_coeff(&srcData[3 * p],  0.500000f, -0.418688f, -0.081312f);
        imgDataNv12[i + 1] = c_coeff(&srcData[3 * p], -0.168736f, -0.331264f,  0.500000f);
      }
    }

    D3D11_SUBRESOURCE_DATA subresourceData = { };
    subresourceData.pSysMem = imgDataRgba.data();
    subresourceData.SysMemPitch = rowSizeRgba;
    subresourceData.SysMemSlicePitch = rowSizeRgba * textureDesc.Height;

    if (FAILED(hr = m_device->CreateTexture2D(&textureDesc, &subresourceData, &m_videoInput))) {
      std::cerr << "Failed to create D3D11 video input image" << std::endl;
      return;
    }

    D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc = { };
    inputDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
    inputDesc.Texture2D.MipSlice = 0;

    if (FAILED(hr = m_vdevice->CreateVideoProcessorInputView(m_videoInput.ptr(), m_venum.ptr(), &inputDesc, &m_videoInputView))) {
      std::cerr << "Failed to create D3D11 video input view" << std::endl;
      return;
    }

    // NV12 input image and view
    textureDesc.Format = DXGI_FORMAT_NV12;
    textureDesc.BindFlags = 0;

    subresourceData.pSysMem = imgDataNv12.data();
    subresourceData.SysMemPitch = rowSizeNv12;
    subresourceData.SysMemSlicePitch = rowSizeNv12 * textureDesc.Height;

    if (SUCCEEDED(hr = m_device->CreateTexture2D(&textureDesc, nullptr, &m_videoInputNv12))) {
      if (FAILED(hr = m_vdevice->CreateVideoProcessorInputView(m_videoInputNv12.ptr(), m_venum.ptr(), &inputDesc, &m_videoInputViewNv12))) {
        std::cerr << "Failed to create D3D11 video input view for NV12" << std::endl;
        return;
      }
    } else {
      std::cerr << "NV12 not supported" << std::endl;
    }

    textureDesc.Usage = D3D11_USAGE_STAGING;
    textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;

    if (SUCCEEDED(hr = m_device->CreateTexture2D(&textureDesc, nullptr, &m_videoInputNv12Host))) {
      D3D11_MAPPED_SUBRESOURCE mr = { };
      m_context->Map(m_videoInputNv12Host.ptr(), 0, D3D11_MAP_WRITE, D3D11_MAP_FLAG_DO_NOT_WAIT, &mr);
      memcpy(mr.pData, imgDataNv12.data(), imgDataNv12.size());
      m_context->Unmap(m_videoInputNv12Host.ptr(), 0);
      D3D11_BOX box = { 0, 0, 0, 128, 128, 1 };
      m_context->CopySubresourceRegion(m_videoInputNv12.ptr(), 0, 0, 0, 0, m_videoInputNv12Host.ptr(), 0, &box);
    }

    // YUY2 input image and view
    textureDesc.Format = DXGI_FORMAT_YUY2;
    textureDesc.BindFlags = 0;
    textureDesc.Usage = D3D11_USAGE_DEFAULT;
    textureDesc.CPUAccessFlags = 0;

    subresourceData.pSysMem = imgDataYuy2.data();
    subresourceData.SysMemPitch = rowSizeYuy2;
    subresourceData.SysMemSlicePitch = imageSizeYuy2;

    if (SUCCEEDED(hr = m_device->CreateTexture2D(&textureDesc, &subresourceData, &m_videoInputYuy2))) {
      if (FAILED(hr = m_vdevice->CreateVideoProcessorInputView(m_videoInputYuy2.ptr(), m_venum.ptr(), &inputDesc, &m_videoInputViewYuy2))) {
        std::cerr << "Failed to create D3D11 video input view for YUY2" << std::endl;
        return;
      }
    } else {
      std::cerr << "YUY2 not supported" << std::endl;
    }

    m_initialized = true;
  }

  ~VideoApp() {

  }

  void run() {
    this->adjustBackBuffer();

    float color[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
    m_context->ClearRenderTargetView(m_swapImageView.ptr(), color);

    // Full range RGB output color space
    D3D11_VIDEO_PROCESSOR_COLOR_SPACE csOut = { };
    csOut.Usage     = 0; // Present
    csOut.RGB_Range = 0; // Full range
    csOut.Nominal_Range = 1; // Full range

    D3D11_VIDEO_PROCESSOR_COLOR_SPACE csIn = { };
    csIn.Usage     = 0; // Present
    csIn.RGB_Range = 0; // Full range
    csIn.Nominal_Range = 1; // Full range
    csIn.YCbCr_Matrix = 0; // BT.601

    m_vcontext->VideoProcessorSetStreamAutoProcessingMode(m_vprocessor.ptr(), 0, false);
    m_vcontext->VideoProcessorSetOutputColorSpace(m_vprocessor.ptr(), &csOut);
    m_vcontext->VideoProcessorSetStreamColorSpace(m_vprocessor.ptr(), 0, &csIn);
    blit(m_videoInputView.ptr(), 32, 32);
    blit(m_videoInputViewNv12.ptr(), 32, 320);
    blit(m_videoInputViewYuy2.ptr(), 32, 608);

    csIn.RGB_Range = 1; // Limited range
    csIn.Nominal_Range = 0; // Limited range
    m_vcontext->VideoProcessorSetStreamColorSpace(m_vprocessor.ptr(), 0, &csIn);
    blit(m_videoInputView.ptr(), 320, 32);
    blit(m_videoInputViewNv12.ptr(), 320, 320);
    blit(m_videoInputViewYuy2.ptr(), 320, 608);

    // Limited range RGB output color space
    csOut.RGB_Range = 1;
    csOut.Nominal_Range = 0;
    m_vcontext->VideoProcessorSetOutputColorSpace(m_vprocessor.ptr(), &csOut);

    csIn.RGB_Range = 0; // Full range
    csIn.Nominal_Range = 1; // Full range
    m_vcontext->VideoProcessorSetStreamColorSpace(m_vprocessor.ptr(), 0, &csIn);
    blit(m_videoInputView.ptr(), 608, 32);
    blit(m_videoInputViewNv12.ptr(), 608, 320);
    blit(m_videoInputViewYuy2.ptr(), 608, 608);

    csIn.RGB_Range = 1; // Limited range
    csIn.Nominal_Range = 0; // Limited range
    m_vcontext->VideoProcessorSetStreamColorSpace(m_vprocessor.ptr(), 0, &csIn);
    blit(m_videoInputView.ptr(), 896, 32);
    blit(m_videoInputViewNv12.ptr(), 896, 320);
    blit(m_videoInputViewYuy2.ptr(), 896, 608);

    m_swapchain->Present(1, 0);
  }

  void blit(ID3D11VideoProcessorInputView* pView, uint32_t x, uint32_t y) {
    if (!pView)
      return;

    D3D11_VIDEO_PROCESSOR_STREAM stream = { };
    stream.Enable = true;
    stream.pInputSurface = pView;

    D3D11_BOX box;
    box.left = 0;
    box.top = 0;
    box.front = 0;
    box.right = 256;
    box.bottom = 256;
    box.back = 1;

    FLOAT red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
    m_context->ClearRenderTargetView(m_videoOutputRtv.ptr(), red);
    m_vcontext->VideoProcessorBlt(m_vprocessor.ptr(), m_videoOutputView.ptr(), 0, 1, &stream);
    m_context->CopySubresourceRegion(m_swapImage.ptr(), 0, x, y, 0, m_videoOutput.ptr(), 0, &box);
  }

  void adjustBackBuffer() {
    RECT windowRect = { };
    GetClientRect(m_window, &windowRect);

    if (uint32_t(windowRect.right - windowRect.left) != m_windowSizeX
     || uint32_t(windowRect.bottom - windowRect.top) != m_windowSizeY) {
      m_windowSizeX = windowRect.right - windowRect.left;
      m_windowSizeY = windowRect.bottom - windowRect.top;

      m_swapImage = nullptr;
      m_swapImageView = nullptr;

      HRESULT hr = m_swapchain->ResizeBuffers(0,
        m_windowSizeX, m_windowSizeY, DXGI_FORMAT_UNKNOWN, 0);

      if (FAILED(hr)) {
        std::cerr << "Failed to resize swap chain buffer" << std::endl;
        return;
      }

      if (FAILED(hr = m_swapchain->GetBuffer(0, IID_PPV_ARGS(&m_swapImage)))) {
        std::cerr << "Failed to query swap chain image" << std::endl;
        return;
      }

      if (FAILED(hr = m_device->CreateRenderTargetView(m_swapImage.ptr(), nullptr, &m_swapImageView))) {
        std::cerr << "Failed to create render target view" << std::endl;
        return;
      }
    }
  }

  operator bool () const {
    return m_initialized;
  }

private:

  HWND                                m_window;
  uint32_t                            m_windowSizeX = 1280;
  uint32_t                            m_windowSizeY = 720;

  Com<IDXGISwapChain>                 m_swapchain;
  Com<ID3D11Device>                   m_device;
  Com<ID3D11DeviceContext>            m_context;
  Com<ID3D11VideoDevice>              m_vdevice;
  Com<ID3D11VideoContext>             m_vcontext;
  Com<ID3D11VideoProcessorEnumerator> m_venum;
  Com<ID3D11VideoProcessor>           m_vprocessor;
  Com<ID3D11Texture2D>                m_swapImage;
  Com<ID3D11RenderTargetView>         m_swapImageView;
  Com<ID3D11Texture2D>                m_videoOutput;
  Com<ID3D11VideoProcessorOutputView> m_videoOutputView;
  Com<ID3D11RenderTargetView>         m_videoOutputRtv;
  Com<ID3D11Texture2D>                m_videoInput;
  Com<ID3D11VideoProcessorInputView>  m_videoInputView;
  Com<ID3D11Texture2D>                m_videoInputNv12;
  Com<ID3D11Texture2D>                m_videoInputNv12Host;
  Com<ID3D11Texture2D>                m_videoInputYuy2;
  Com<ID3D11VideoProcessorInputView>  m_videoInputViewNv12;
  Com<ID3D11VideoProcessorInputView>  m_videoInputViewYuy2;

  bool                                m_initialized = false;

  static inline uint8_t y_coeff(const uint8_t* rgb, float r, float g, float b) {
    float x = (rgb[0] * r + rgb[1] * g + rgb[2] * b) / 255.0f;
    return 16 + uint8_t(std::roundf(219.0f * std::clamp(x, 0.0f, 1.0f)));
  }

  static inline uint8_t c_coeff(const uint8_t* rgb, float r, float g, float b) {
    float x = ((rgb[0] * r + rgb[1] * g + rgb[2] * b) / 255.0f) + 0.5f;
    return uint8_t(std::roundf(255.0f * std::clamp(x, 0.0f, 1.0f)));
  }

};

LRESULT CALLBACK WindowProc(HWND hWnd,
                            UINT message,
                            WPARAM wParam,
                            LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
  HWND hWnd;
  WNDCLASSEXW wc;
  ZeroMemory(&wc, sizeof(WNDCLASSEX));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
  wc.lpszClassName = L"WindowClass1";
  RegisterClassExW(&wc);

  hWnd = CreateWindowExW(0,
    L"WindowClass1",
    L"Our First Windowed Program",
    WS_OVERLAPPEDWINDOW,
    300, 300,
    1280, 720,
    nullptr,
    nullptr,
    hInstance,
    nullptr);
  ShowWindow(hWnd, nCmdShow);

  MSG msg;
  VideoApp app(hInstance, hWnd);

  while (app) {
    if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);

      if (msg.message == WM_QUIT)
        return msg.wParam;
    } else {
      app.run();
    }
  }

  return 0;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  switch (message) {
    case WM_CLOSE:
      PostQuitMessage(0);
      return 0;
  }

  return DefWindowProc(hWnd, message, wParam, lParam);
}

repo_name: orochi/dxvk


code:
test_d3d9_deps = [ util_dep, lib_d3d9, lib_d3dcompiler_47 ]

executable('d3d9-clear'+exe_ext,  files('test_d3d9_clear.cpp'),  dependencies : test_d3d9_deps, install : true, gui_app : true)
executable('d3d9-buffer'+exe_ext,  files('test_d3d9_buffer.cpp'),  dependencies : test_d3d9_deps, install : true, gui_app : true)
executable('d3d9-triangle'+exe_ext,  files('test_d3d9_triangle.cpp'),  dependencies : test_d3d9_deps, install : true, gui_app : true)
executable('d3d9-l6v5u5'+exe_ext,  files('test_d3d9_l6v5u5.cpp'),  dependencies : test_d3d9_deps, install : true, gui_app : true)
executable('d3d9-nv12'+exe_ext,  files('test_d3d9_nv12.cpp'),  dependencies : test_d3d9_deps, install : true, gui_app : true)
executable('d3d9-bc-update-surface'+exe_ext,  files('test_d3d9_bc_update_surface.cpp'),  dependencies : test_d3d9_deps, install : true, gui_app : true)
executable('d3d9-up'+exe_ext,  files('test_d3d9_up.cpp'),  dependencies : test_d3d9_deps, install : true, gui_app : true)


code:
#include <cstring>

#include <d3d9.h>
#include <d3dcompiler.h>

#include "../test_utils.h"

using namespace dxvk;

struct Extent2D {
  uint32_t w, h;
};

const std::string g_vertexShaderCode = R"(

struct VS_INPUT {
  float3 Position : POSITION;
};

struct VS_OUTPUT {
  float4 Position : POSITION;
  float2 Texcoord : TEXCOORD0;
};

VS_OUTPUT main( VS_INPUT IN ) {
  VS_OUTPUT OUT;
  OUT.Position = float4(IN.Position, 0.6f);
  OUT.Texcoord = IN.Position.xy + float2(0.5, 0.5);
  OUT.Texcoord.y = 1.0 - OUT.Texcoord.y;

  return OUT;
}

)";

const std::string g_pixelShaderCode = R"(

struct VS_OUTPUT {
  float4 Position : POSITION;
  float2 Texcoord : TEXCOORD0;
};

struct PS_OUTPUT {
  float4 Colour   : COLOR;
};

sampler g_frogTex : register( s0 );

PS_OUTPUT main( VS_OUTPUT IN ) {
  PS_OUTPUT OUT;

  OUT.Colour = float4(1, 0, 0, 1);

  return OUT;
}

)";

Logger Logger::s_instance("triangle.log");

class TriangleApp {

public:

  TriangleApp(HINSTANCE instance, HWND window)
  : m_window(window) {
    HRESULT status = Direct3DCreate9Ex(D3D_SDK_VERSION, &m_d3d);

    if (FAILED(status))
      throw DxvkError("Failed to create D3D9 interface");

    UINT adapter = D3DADAPTER_DEFAULT;

    D3DADAPTER_IDENTIFIER9 adapterId;
    m_d3d->GetAdapterIdentifier(adapter, 0, &adapterId);

    Logger::info(str::format("Using adapter: ", adapterId.Description));

    D3DPRESENT_PARAMETERS params;
    getPresentParams(params);

    status = m_d3d->CreateDeviceEx(
      adapter,
      D3DDEVTYPE_HAL,
      m_window,
      D3DCREATE_HARDWARE_VERTEXPROCESSING,
      &params,
      nullptr,
      &m_device);

    if (FAILED(status))
        throw DxvkError("Failed to create D3D9 device");

    // Vertex Shader
    {
      Com<ID3DBlob> blob;

      status = D3DCompile(
        g_vertexShaderCode.data(),
        g_vertexShaderCode.length(),
        nullptr, nullptr, nullptr,
        "main",
        "vs_2_0",
        0, 0, &blob,
        nullptr);

      if (FAILED(status))
        throw DxvkError("Failed to compile vertex shader");

      status = m_device->CreateVertexShader(reinterpret_cast<const DWORD*>(blob->GetBufferPointer()), &m_vs);

      if (FAILED(status))
        throw DxvkError("Failed to create vertex shader");
    }

    // Pixel Shader
    {
      Com<ID3DBlob> blob;

      status = D3DCompile(
        g_pixelShaderCode.data(),
        g_pixelShaderCode.length(),
        nullptr, nullptr, nullptr,
        "main",
        "ps_3_0",
        0, 0, &blob,
        nullptr);

      if (FAILED(status))
        throw DxvkError("Failed to compile pixel shader");

      status = m_device->CreatePixelShader(reinterpret_cast<const DWORD*>(blob->GetBufferPointer()), &m_ps);

      if (FAILED(status))
        throw DxvkError("Failed to create pixel shader");
    }

    m_device->SetVertexShader(m_vs.ptr());
    m_device->SetPixelShader(m_ps.ptr());

    m_device->AddRef();

    std::array<float, 9> vertices = {
      0.0f, 0.5f, 0.0f,
      0.5f, -0.5f, 0.0f,
      -0.5f, -0.5f, 0.0f,
    };

    const size_t vbSize = vertices.size() * sizeof(float);

    status = m_device->CreateVertexBuffer(vbSize, 0, 0, D3DPOOL_DEFAULT, &m_vb, nullptr);
    if (FAILED(status))
      throw DxvkError("Failed to create vertex buffer");

    void* data = nullptr;
    status = m_vb->Lock(0, 0, &data, 0);
    if (FAILED(status))
      throw DxvkError("Failed to lock vertex buffer");

    std::memcpy(data, vertices.data(), vbSize);

    status = m_vb->Unlock();
    if (FAILED(status))
      throw DxvkError("Failed to unlock vertex buffer");

    m_device->SetStreamSource(0, m_vb.ptr(), 0, 3 * sizeof(float));

    std::array<D3DVERTEXELEMENT9, 2> elements;

    elements[0].Method = 0;
    elements[0].Offset = 0;
    elements[0].Stream = 0;
    elements[0].Type = D3DDECLTYPE_FLOAT3;
    elements[0].Usage = D3DDECLUSAGE_POSITION;
    elements[0].UsageIndex = 0;

    elements[1] = D3DDECL_END();

    HRESULT result = m_device->CreateVertexDeclaration(elements.data(), &m_decl);
    if (FAILED(result))
      throw DxvkError("Failed to create vertex decl");

    m_device->SetVertexDeclaration(m_decl.ptr());

    const uint32_t imageSize = 8;

    Com<IDirect3DTexture9> texDefault;
    Com<IDirect3DSurface9> texDefaultSurf;
    status = m_device->CreateTexture(imageSize, imageSize, 4, 0, D3DFMT_DXT1, D3DPOOL_DEFAULT, &texDefault, nullptr);
    status = texDefault->GetSurfaceLevel(2, &texDefaultSurf);

    Com<IDirect3DTexture9> texSysmem;
    Com<IDirect3DSurface9> texSysmemSurf;
    status = m_device->CreateTexture(imageSize, imageSize, 4, 0, D3DFMT_DXT1, D3DPOOL_SYSTEMMEM, &texSysmem, nullptr);
    status = texSysmem->GetSurfaceLevel(2, &texSysmemSurf);

    D3DLOCKED_RECT rect = {};
    status = texSysmemSurf->LockRect(&rect, nullptr, 0);
    std::memset(rect.pBits, 0xF0, 2 * rect.Pitch);
    status = texSysmemSurf->UnlockRect();

    // 1 returns invalid call
    // 2 succeeds
    RECT sourceRect = { 0, 0, 2, 2 };
    POINT destPoint = { 0, 0 };
    status = m_device->UpdateSurface(texSysmemSurf.ptr(), &sourceRect, texDefaultSurf.ptr(), &destPoint);
  }

  void run() {
    this->adjustBackBuffer();

    m_device->BeginScene();

    m_device->Clear(
      0,
      nullptr,
      D3DCLEAR_TARGET,
      D3DCOLOR_RGBA(44, 62, 80, 0),
      0,
      0);

    m_device->Clear(
      0,
      nullptr,
      D3DCLEAR_ZBUFFER,
      0,
      0.5f,
      0);

    m_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

    m_device->EndScene();

    m_device->PresentEx(
      nullptr,
      nullptr,
      nullptr,
      nullptr,
      0);
  }

  void adjustBackBuffer() {
    RECT windowRect = { 0, 0, 1024, 600 };
    GetClientRect(m_window, &windowRect);

    Extent2D newSize = {
      static_cast<uint32_t>(windowRect.right - windowRect.left),
      static_cast<uint32_t>(windowRect.bottom - windowRect.top),
    };

    if (m_windowSize.w != newSize.w
     || m_windowSize.h != newSize.h) {
      m_windowSize = newSize;

      D3DPRESENT_PARAMETERS params;
      getPresentParams(params);
      HRESULT status = m_device->ResetEx(&params, nullptr);

      if (FAILED(status))
        throw DxvkError("Device reset failed");
    }
  }

  void getPresentParams(D3DPRESENT_PARAMETERS& params) {
    params.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
    params.BackBufferCount = 1;
    params.BackBufferFormat = D3DFMT_X8R8G8B8;
    params.BackBufferWidth = m_windowSize.w;
    params.BackBufferHeight = m_windowSize.h;
    params.EnableAutoDepthStencil = 0;
    params.Flags = 0;
    params.FullScreen_RefreshRateInHz = 0;
    params.hDeviceWindow = m_window;
    params.MultiSampleQuality = 0;
    params.MultiSampleType = D3DMULTISAMPLE_NONE;
    params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
    params.SwapEffect = D3DSWAPEFFECT_DISCARD;
    params.Windowed = TRUE;
  }

private:

  HWND                          m_window;
  Extent2D                      m_windowSize = { 1024, 600 };

  Com<IDirect3D9Ex>             m_d3d;
  Com<IDirect3DDevice9Ex>       m_device;

  Com<IDirect3DVertexShader9>   m_vs;
  Com<IDirect3DPixelShader9>    m_ps;
  Com<IDirect3DVertexBuffer9>   m_vb;
  Com<IDirect3DVertexDeclaration9> m_decl;

};

LRESULT CALLBACK WindowProc(HWND hWnd,
                            UINT message,
                            WPARAM wParam,
                            LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
  HWND hWnd;
  WNDCLASSEXW wc;
  ZeroMemory(&wc, sizeof(WNDCLASSEX));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
  wc.lpszClassName = L"WindowClass1";
  RegisterClassExW(&wc);

  hWnd = CreateWindowExW(0,
    L"WindowClass1",
    L"Our First Windowed Program",
    WS_OVERLAPPEDWINDOW,
    300, 300,
    640, 480,
    nullptr,
    nullptr,
    hInstance,
    nullptr);
  ShowWindow(hWnd, nCmdShow);

  MSG msg;

  try {
    TriangleApp app(hInstance, hWnd);

    while (true) {
      if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);

        if (msg.message == WM_QUIT)
          return msg.wParam;
      } else {
        app.run();
      }
    }
  } catch (const dxvk::DxvkError& e) {
    std::cerr << e.message() << std::endl;
    return msg.wParam;
  }
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  switch (message) {
    case WM_CLOSE:
      PostQuitMessage(0);
      return 0;
  }

  return DefWindowProc(hWnd, message, wParam, lParam);
}


code:
#include <cstring>
#include <d3d9.h>

#include "../test_utils.h"

using namespace dxvk;

struct Extent2D {
  uint32_t w, h;
};

DWORD g_UsagePermuatations[] = {
  0,
  D3DUSAGE_DYNAMIC,
  D3DUSAGE_WRITEONLY,
  D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
};

DWORD g_MapFlagPermutations[] = {
  0,
  D3DLOCK_DISCARD,
  D3DLOCK_DONOTWAIT,
  D3DLOCK_NOOVERWRITE
};

class BufferApp {

public:

  BufferApp(HINSTANCE instance, HWND window)
  : m_window(window) {
    HRESULT status = Direct3DCreate9Ex(D3D_SDK_VERSION, &m_d3d);

    if (FAILED(status))
      throw DxvkError("Failed to create D3D9 interface");

    D3DPRESENT_PARAMETERS params;
    getPresentParams(params);

    status = m_d3d->CreateDeviceEx(
      D3DADAPTER_DEFAULT,
      D3DDEVTYPE_HAL,
      m_window,
      D3DCREATE_HARDWARE_VERTEXPROCESSING,
      &params,
      nullptr,
      &m_device);

    if (FAILED(status))
      throw DxvkError("Failed to create D3D9 device");

    uint8_t* data = new uint8_t[512];
    std::memset(data, 0xFC, 512);

    for (uint32_t i = 0; i < ARRAYSIZE(g_UsagePermuatations); i++) {
      for (uint32_t j = 0; j < ARRAYSIZE(g_MapFlagPermutations); j++) {
        testBuffer(data, g_UsagePermuatations[i], g_MapFlagPermutations[j]);
      }
    }

    delete[] data;
  }

  void testBuffer(uint8_t* data, DWORD usage, DWORD mapFlags) {
    Com<IDirect3DVertexBuffer9> buffer;
    HRESULT status = m_device->CreateVertexBuffer(512, usage, 0, D3DPOOL_DEFAULT, &buffer, nullptr);

    if (FAILED(status))
      throw DxvkError("Failed to create buffer");

    void* bufferMem = nullptr;
    status = buffer->Lock(0, 0, &bufferMem, mapFlags);

    if (FAILED(status) || bufferMem == nullptr)
      throw DxvkError("Failed to lock buffer");

    std::memcpy(bufferMem, data, 512);

    status = buffer->Unlock();

    if (FAILED(status))
      throw DxvkError("Failed to unlock buffer");
  }

  void run() {
    this->adjustBackBuffer();

    m_device->BeginScene();

    m_device->Clear(
      0,
      nullptr,
      D3DCLEAR_TARGET,
      D3DCOLOR_RGBA(255, 50, 139, 0),
      0.0f,
      0);

    m_device->EndScene();

    m_device->PresentEx(
      nullptr,
      nullptr,
      nullptr,
      nullptr,
      0);
  }

  void adjustBackBuffer() {
    RECT windowRect = { 0, 0, 1024, 600 };
    GetClientRect(m_window, &windowRect);

    Extent2D newSize = {
      static_cast<uint32_t>(windowRect.right - windowRect.left),
      static_cast<uint32_t>(windowRect.bottom - windowRect.top),
    };

    if (m_windowSize.w != newSize.w
     || m_windowSize.h != newSize.h) {
      m_windowSize = newSize;

      D3DPRESENT_PARAMETERS params;
      getPresentParams(params);
      HRESULT status = m_device->ResetEx(&params, nullptr);

      if (FAILED(status))
        throw DxvkError("Device reset failed");
    }
  }

  void getPresentParams(D3DPRESENT_PARAMETERS& params) {
    params.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
    params.BackBufferCount = 1;
    params.BackBufferFormat = D3DFMT_X8R8G8B8;
    params.BackBufferWidth = m_windowSize.w;
    params.BackBufferHeight = m_windowSize.h;
    params.EnableAutoDepthStencil = FALSE;
    params.Flags = 0;
    params.FullScreen_RefreshRateInHz = 0;
    params.hDeviceWindow = m_window;
    params.MultiSampleQuality = 0;
    params.MultiSampleType = D3DMULTISAMPLE_NONE;
    params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
    params.SwapEffect = D3DSWAPEFFECT_DISCARD;
    params.Windowed = TRUE;
  }

private:

  HWND                          m_window;
  Extent2D                      m_windowSize = { 1024, 600 };

  Com<IDirect3D9Ex>             m_d3d;
  Com<IDirect3DDevice9Ex>       m_device;

};

LRESULT CALLBACK WindowProc(HWND hWnd,
                            UINT message,
                            WPARAM wParam,
                            LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
  HWND hWnd;
  WNDCLASSEXW wc;
  ZeroMemory(&wc, sizeof(WNDCLASSEX));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
  wc.lpszClassName = L"WindowClass1";
  RegisterClassExW(&wc);

  hWnd = CreateWindowExW(0,
    L"WindowClass1",
    L"Our First Windowed Program",
    WS_OVERLAPPEDWINDOW,
    300, 300,
    640, 480,
    nullptr,
    nullptr,
    hInstance,
    nullptr);
  ShowWindow(hWnd, nCmdShow);

  MSG msg;

  try {
    BufferApp app(hInstance, hWnd);

    while (true) {
      if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);

        if (msg.message == WM_QUIT)
          return msg.wParam;
      } else {
        app.run();
      }
    }
  } catch (const dxvk::DxvkError& e) {
    std::cerr << e.message() << std::endl;
    return msg.wParam;
  }
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  switch (message) {
    case WM_CLOSE:
      PostQuitMessage(0);
      return 0;
  }

  return DefWindowProc(hWnd, message, wParam, lParam);
}


code:
#include <d3d9.h>

#include "../test_utils.h"

using namespace dxvk;

struct Extent2D {
  uint32_t w, h;
};

class ClearApp {

public:

  ClearApp(HINSTANCE instance, HWND window)
  : m_window(window) {
    HRESULT status = Direct3DCreate9Ex(D3D_SDK_VERSION, &m_d3d);

    if (FAILED(status))
      throw DxvkError("Failed to create D3D9 interface");

    D3DPRESENT_PARAMETERS params;
    getPresentParams(params);

    status = m_d3d->CreateDeviceEx(
      D3DADAPTER_DEFAULT,
      D3DDEVTYPE_HAL,
      m_window,
      D3DCREATE_HARDWARE_VERTEXPROCESSING,
      &params,
      nullptr,
      &m_device);

    if (FAILED(status))
      throw DxvkError("Failed to create D3D9 device");
  }

  void run() {
    this->adjustBackBuffer();

    m_device->BeginScene();

    m_device->Clear(
      0,
      nullptr,
      D3DCLEAR_TARGET,
      D3DCOLOR_RGBA(255, 0, 0, 0),
      0.0f,
      0);

    m_device->EndScene();

    m_device->PresentEx(
      nullptr,
      nullptr,
      nullptr,
      nullptr,
      0);
  }

  void adjustBackBuffer() {
    RECT windowRect = { 0, 0, 1024, 600 };
    GetClientRect(m_window, &windowRect);

    Extent2D newSize = {
      static_cast<uint32_t>(windowRect.right - windowRect.left),
      static_cast<uint32_t>(windowRect.bottom - windowRect.top),
    };

    if (m_windowSize.w != newSize.w
     || m_windowSize.h != newSize.h) {
      m_windowSize = newSize;

      D3DPRESENT_PARAMETERS params;
      getPresentParams(params);
      HRESULT status = m_device->ResetEx(&params, nullptr);

      if (FAILED(status))
        throw DxvkError("Device reset failed");
    }
  }

  void getPresentParams(D3DPRESENT_PARAMETERS& params) {
    params.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
    params.BackBufferCount = 1;
    params.BackBufferFormat = D3DFMT_X8R8G8B8;
    params.BackBufferWidth = m_windowSize.w;
    params.BackBufferHeight = m_windowSize.h;
    params.EnableAutoDepthStencil = FALSE;
    params.Flags = 0;
    params.FullScreen_RefreshRateInHz = 0;
    params.hDeviceWindow = m_window;
    params.MultiSampleQuality = 0;
    params.MultiSampleType = D3DMULTISAMPLE_NONE;
    params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
    params.SwapEffect = D3DSWAPEFFECT_DISCARD;
    params.Windowed = TRUE;
  }

private:

  HWND                          m_window;
  Extent2D                      m_windowSize = { 1024, 600 };

  Com<IDirect3D9Ex>             m_d3d;
  Com<IDirect3DDevice9Ex>       m_device;

};

LRESULT CALLBACK WindowProc(HWND hWnd,
                            UINT message,
                            WPARAM wParam,
                            LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
  HWND hWnd;
  WNDCLASSEXW wc;
  ZeroMemory(&wc, sizeof(WNDCLASSEX));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
  wc.lpszClassName = L"WindowClass1";
  RegisterClassExW(&wc);

  hWnd = CreateWindowExW(0,
    L"WindowClass1",
    L"Our First Windowed Program",
    WS_OVERLAPPEDWINDOW,
    300, 300,
    640, 480,
    nullptr,
    nullptr,
    hInstance,
    nullptr);
  ShowWindow(hWnd, nCmdShow);

  MSG msg;

  try {
    ClearApp app(hInstance, hWnd);

    while (true) {
      if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);

        if (msg.message == WM_QUIT)
          return msg.wParam;
      } else {
        app.run();
      }
    }
  } catch (const dxvk::DxvkError& e) {
    std::cerr << e.message() << std::endl;
    return msg.wParam;
  }
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  switch (message) {
    case WM_CLOSE:
      PostQuitMessage(0);
      return 0;
  }

  return DefWindowProc(hWnd, message, wParam, lParam);
}


code:
#include <cstring>

#include <d3d9.h>
#include <d3dcompiler.h>

#include "../test_utils.h"

using namespace dxvk;

struct Extent2D {
  uint32_t w, h;
};

const std::string g_vertexShaderCode = R"(

struct VS_INPUT {
  float3 Position : POSITION;
};

struct VS_OUTPUT {
  float4 Position : POSITION;
};

VS_OUTPUT main( VS_INPUT IN ) {
  VS_OUTPUT OUT;
  OUT.Position = float4(IN.Position, 1.0f);

  return OUT;
}

)";

const std::string g_pixelShaderCode = R"(

struct VS_OUTPUT {
  float4 Position : POSITION;
};

struct PS_OUTPUT {
  float4 Colour   : COLOR;
};

sampler g_tex : register( s0 );

PS_OUTPUT main( VS_OUTPUT IN ) {
  PS_OUTPUT OUT;

  float4 color = float4(tex2D(g_tex, float2(0.5, 0.5)).rgb, 1.0f);
  color.r = -color.r;
  color.g = -color.g;
  OUT.Colour = color;

  return OUT;
}

)";

class TriangleApp {

public:

  TriangleApp(HINSTANCE instance, HWND window)
  : m_window(window) {
    HRESULT status = Direct3DCreate9Ex(D3D_SDK_VERSION, &m_d3d);

    if (FAILED(status))
      throw DxvkError("Failed to create D3D9 interface");

    D3DPRESENT_PARAMETERS params;
    getPresentParams(params);

    status = m_d3d->CreateDeviceEx(
      D3DADAPTER_DEFAULT,
      D3DDEVTYPE_HAL,
      m_window,
      D3DCREATE_HARDWARE_VERTEXPROCESSING,
      &params,
      nullptr,
      &m_device);

    if (FAILED(status))
      throw DxvkError("Failed to create D3D9 device");

    // Vertex Shader
    {
      Com<ID3DBlob> blob;

      status = D3DCompile(
        g_vertexShaderCode.data(),
        g_vertexShaderCode.length(),
        nullptr, nullptr, nullptr,
        "main",
        "vs_2_0",
        0, 0, &blob,
        nullptr);

      if (FAILED(status))
        throw DxvkError("Failed to compile vertex shader");

      status = m_device->CreateVertexShader(reinterpret_cast<const DWORD*>(blob->GetBufferPointer()), &m_vs);

      if (FAILED(status))
        throw DxvkError("Failed to create vertex shader");
    }

    // Pixel Shader
    {
      Com<ID3DBlob> blob;

      status = D3DCompile(
        g_pixelShaderCode.data(),
        g_pixelShaderCode.length(),
        nullptr, nullptr, nullptr,
        "main",
        "ps_2_0",
        0, 0, &blob,
        nullptr);

      if (FAILED(status))
        throw DxvkError("Failed to compile pixel shader");

      status = m_device->CreatePixelShader(reinterpret_cast<const DWORD*>(blob->GetBufferPointer()), &m_ps);

      if (FAILED(status))
        throw DxvkError("Failed to create pixel shader");
    }

    m_device->SetVertexShader(m_vs.ptr());
    m_device->SetPixelShader(m_ps.ptr());

    std::array<float, 9> vertices = {
      0.0f, 0.5f, 0.0f,
      0.5f, -0.5f, 0.0f,
      -0.5f, -0.5f, 0.0f,
    };

    const size_t vbSize = vertices.size() * sizeof(float);

    status = m_device->CreateVertexBuffer(vbSize, 0, 0, D3DPOOL_DEFAULT, &m_vb, nullptr);
    if (FAILED(status))
      throw DxvkError("Failed to create vertex buffer");

    void* data = nullptr;
    status = m_vb->Lock(0, 0, &data, 0);
    if (FAILED(status))
      throw DxvkError("Failed to lock vertex buffer");

    std::memcpy(data, vertices.data(), vbSize);

    status = m_vb->Unlock();
    if (FAILED(status))
      throw DxvkError("Failed to unlock vertex buffer");

    m_device->SetStreamSource(0, m_vb.ptr(), 0, 3 * sizeof(float));

    std::array<D3DVERTEXELEMENT9, 2> elements;

    elements[0].Method = 0;
    elements[0].Offset = 0;
    elements[0].Stream = 0;
    elements[0].Type = D3DDECLTYPE_FLOAT3;
    elements[0].Usage = D3DDECLUSAGE_POSITION;
    elements[0].UsageIndex = 0;

    elements[1] = D3DDECL_END();

    HRESULT result = m_device->CreateVertexDeclaration(elements.data(), &m_decl);
    if (FAILED(result))
      throw DxvkError("Failed to create vertex decl");

    m_device->SetVertexDeclaration(m_decl.ptr());

    // The actual texture we want to test...

    Com<IDirect3DTexture9> texture;
    status = m_device->CreateTexture(64, 64, 1, D3DUSAGE_DYNAMIC, D3DFMT_L6V5U5, D3DPOOL_DEFAULT, &texture, nullptr);

    D3DLOCKED_RECT rect;
    status = texture->LockRect(0, &rect, nullptr, 0);

    uint16_t* texData = reinterpret_cast<uint16_t*>(rect.pBits);
    for (uint32_t i = 0; i < (rect.Pitch * 64) / sizeof(uint16_t); i++) {
      // -> U -1, V -1, L 1
      texData[i] = 0b1111111000010000;
      // -> U 1, V 1, L 1
      //texData[i] = 0b1111110111101111;
    }

    status = texture->UnlockRect(0);

    status = m_device->SetTexture(0, texture.ptr());

    /////////////

    /*Com<IDirect3DTexture9> texture2;
    status = m_device->CreateTexture(64, 64, 1, 0, D3DFMT_A8B8G8R8, D3DPOOL_MANAGED, &texture2, nullptr);
    status = texture2->LockRect(0, &rect, nullptr, 0);

    uint32_t* texData2 = reinterpret_cast<uint32_t*>(rect.pBits);
    for (uint32_t i = 0; i < (rect.Pitch * 64) / sizeof(uint32_t); i++) {
      texData2[i] = 0b00000000000000000000000011111111;
    }

    status = texture2->UnlockRect(0);

    status = m_device->SetTexture(0, texture2.ptr());*/
  }

  void run() {
    this->adjustBackBuffer();

    m_device->BeginScene();

    m_device->Clear(
      0,
      nullptr,
      D3DCLEAR_TARGET,
      D3DCOLOR_RGBA(44, 62, 80, 0),
      0,
      0);

    m_device->Clear(
      0,
      nullptr,
      D3DCLEAR_ZBUFFER,
      0,
      0.5f,
      0);

    m_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

    m_device->EndScene();

    m_device->PresentEx(
      nullptr,
      nullptr,
      nullptr,
      nullptr,
      0);
  }

  void adjustBackBuffer() {
    RECT windowRect = { 0, 0, 1024, 600 };
    GetClientRect(m_window, &windowRect);

    Extent2D newSize = {
      static_cast<uint32_t>(windowRect.right - windowRect.left),
      static_cast<uint32_t>(windowRect.bottom - windowRect.top),
    };

    if (m_windowSize.w != newSize.w
     || m_windowSize.h != newSize.h) {
      m_windowSize = newSize;

      D3DPRESENT_PARAMETERS params;
      getPresentParams(params);
      HRESULT status = m_device->ResetEx(&params, nullptr);

      if (FAILED(status))
        throw DxvkError("Device reset failed");
    }
  }

  void getPresentParams(D3DPRESENT_PARAMETERS& params) {
    params.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
    params.BackBufferCount = 1;
    params.BackBufferFormat = D3DFMT_X8R8G8B8;
    params.BackBufferWidth = m_windowSize.w;
    params.BackBufferHeight = m_windowSize.h;
    params.EnableAutoDepthStencil = 0;
    params.Flags = 0;
    params.FullScreen_RefreshRateInHz = 0;
    params.hDeviceWindow = m_window;
    params.MultiSampleQuality = 0;
    params.MultiSampleType = D3DMULTISAMPLE_NONE;
    params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
    params.SwapEffect = D3DSWAPEFFECT_DISCARD;
    params.Windowed = TRUE;
  }

private:

  HWND                          m_window;
  Extent2D                      m_windowSize = { 1024, 600 };

  Com<IDirect3D9Ex>             m_d3d;
  Com<IDirect3DDevice9Ex>       m_device;

  Com<IDirect3DVertexShader9>   m_vs;
  Com<IDirect3DPixelShader9>    m_ps;
  Com<IDirect3DVertexBuffer9>   m_vb;
  Com<IDirect3DVertexDeclaration9> m_decl;

};

LRESULT CALLBACK WindowProc(HWND hWnd,
                            UINT message,
                            WPARAM wParam,
                            LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
  HWND hWnd;
  WNDCLASSEXW wc;
  ZeroMemory(&wc, sizeof(WNDCLASSEX));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
  wc.lpszClassName = L"WindowClass1";
  RegisterClassExW(&wc);

  hWnd = CreateWindowExW(0,
    L"WindowClass1",
    L"Our First Windowed Program",
    WS_OVERLAPPEDWINDOW,
    300, 300,
    640, 480,
    nullptr,
    nullptr,
    hInstance,
    nullptr);
  ShowWindow(hWnd, nCmdShow);

  MSG msg;

  try {
    TriangleApp app(hInstance, hWnd);

    while (true) {
      if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);

        if (msg.message == WM_QUIT)
          return msg.wParam;
      } else {
        app.run();
      }
    }
  } catch (const dxvk::DxvkError& e) {
    std::cerr << e.message() << std::endl;
    return msg.wParam;
  }
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  switch (message) {
    case WM_CLOSE:
      PostQuitMessage(0);
      return 0;
  }

  return DefWindowProc(hWnd, message, wParam, lParam);
}


code:
#include <cstring>

#include <d3d9.h>
#include <d3dcompiler.h>

#include "../test_utils.h"
#include "test_d3d9_nv12.yuv.h"

using namespace dxvk;

struct Extent2D {
  uint32_t w, h;
};

const std::string g_vertexShaderCode = R"(

struct VS_INPUT {
  float3 Position : POSITION;
};

struct VS_OUTPUT {
  float4 Position : POSITION;
  float2 Texcoord : TEXCOORD0;
};

VS_OUTPUT main( VS_INPUT IN ) {
  VS_OUTPUT OUT;
  OUT.Position = float4(IN.Position, 0.6f);
  OUT.Texcoord = IN.Position.xy + float2(0.5, 0.5);
  OUT.Texcoord.y = 1.0 - OUT.Texcoord.y;

  return OUT;
}

)";

const std::string g_pixelShaderCode = R"(

struct VS_OUTPUT {
  float4 Position : POSITION;
  float2 Texcoord : TEXCOORD0;
};

struct PS_OUTPUT {
  float4 Colour   : COLOR;
};

sampler g_frogTex : register( s0 );

PS_OUTPUT main( VS_OUTPUT IN ) {
  PS_OUTPUT OUT;

  OUT.Colour = tex2D(g_frogTex, IN.Texcoord);

  return OUT;
}

)";

Logger Logger::s_instance("triangle.log");

class TriangleApp {

public:

  TriangleApp(HINSTANCE instance, HWND window)
  : m_window(window) {
    HRESULT status = Direct3DCreate9Ex(D3D_SDK_VERSION, &m_d3d);

    if (FAILED(status))
      throw DxvkError("Failed to create D3D9 interface");

    UINT adapter = D3DADAPTER_DEFAULT;

    D3DADAPTER_IDENTIFIER9 adapterId;
    m_d3d->GetAdapterIdentifier(adapter, 0, &adapterId);

    Logger::info(str::format("Using adapter: ", adapterId.Description));

    D3DPRESENT_PARAMETERS params;
    getPresentParams(params);

    status = m_d3d->CreateDeviceEx(
      adapter,
      D3DDEVTYPE_HAL,
      m_window,
      D3DCREATE_HARDWARE_VERTEXPROCESSING,
      &params,
      nullptr,
      &m_device);

    if (FAILED(status))
        throw DxvkError("Failed to create D3D9 device");

    // Vertex Shader
    {
      Com<ID3DBlob> blob;

      status = D3DCompile(
        g_vertexShaderCode.data(),
        g_vertexShaderCode.length(),
        nullptr, nullptr, nullptr,
        "main",
        "vs_2_0",
        0, 0, &blob,
        nullptr);

      if (FAILED(status))
        throw DxvkError("Failed to compile vertex shader");

      status = m_device->CreateVertexShader(reinterpret_cast<const DWORD*>(blob->GetBufferPointer()), &m_vs);

      if (FAILED(status))
        throw DxvkError("Failed to create vertex shader");
    }

    // Pixel Shader
    {
      Com<ID3DBlob> blob;

      status = D3DCompile(
        g_pixelShaderCode.data(),
        g_pixelShaderCode.length(),
        nullptr, nullptr, nullptr,
        "main",
        "ps_2_0",
        0, 0, &blob,
        nullptr);

      if (FAILED(status))
        throw DxvkError("Failed to compile pixel shader");

      status = m_device->CreatePixelShader(reinterpret_cast<const DWORD*>(blob->GetBufferPointer()), &m_ps);

      if (FAILED(status))
        throw DxvkError("Failed to create pixel shader");
    }

    m_device->SetVertexShader(m_vs.ptr());
    m_device->SetPixelShader(m_ps.ptr());

    m_device->AddRef();

    std::array<float, 9> vertices = {
      0.0f, 0.5f, 0.0f,
      0.5f, -0.5f, 0.0f,
      -0.5f, -0.5f, 0.0f,
    };

    const size_t vbSize = vertices.size() * sizeof(float);

    status = m_device->CreateVertexBuffer(vbSize, 0, 0, D3DPOOL_DEFAULT, &m_vb, nullptr);
    if (FAILED(status))
      throw DxvkError("Failed to create vertex buffer");

    void* data = nullptr;
    status = m_vb->Lock(0, 0, &data, 0);
    if (FAILED(status))
      throw DxvkError("Failed to lock vertex buffer");

    std::memcpy(data, vertices.data(), vbSize);

    status = m_vb->Unlock();
    if (FAILED(status))
      throw DxvkError("Failed to unlock vertex buffer");

    m_device->SetStreamSource(0, m_vb.ptr(), 0, 3 * sizeof(float));

    std::array<D3DVERTEXELEMENT9, 2> elements;

    elements[0].Method = 0;
    elements[0].Offset = 0;
    elements[0].Stream = 0;
    elements[0].Type = D3DDECLTYPE_FLOAT3;
    elements[0].Usage = D3DDECLUSAGE_POSITION;
    elements[0].UsageIndex = 0;

    elements[1] = D3DDECL_END();

    HRESULT result = m_device->CreateVertexDeclaration(elements.data(), &m_decl);
    if (FAILED(result))
      throw DxvkError("Failed to create vertex decl");

    m_device->SetVertexDeclaration(m_decl.ptr());

    const uint32_t imageSize = 320;

    Com<IDirect3DTexture9> texture;
    Com<IDirect3DSurface9> texSurf;
    status = m_device->CreateTexture(imageSize, imageSize, 1, D3DUSAGE_RENDERTARGET, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &texture, nullptr);
    status = texture->GetSurfaceLevel(0, &texSurf);

    Com<IDirect3DSurface9> nv12Surf;
    status = m_device->CreateOffscreenPlainSurface(imageSize, imageSize, (D3DFORMAT)MAKEFOURCC('N', 'V', '1', '2'), D3DPOOL_DEFAULT, &nv12Surf, nullptr);
    D3DLOCKED_RECT rect;
    nv12Surf->LockRect(&rect, nullptr, 0);
    char* dst = (char*)rect.pBits;
    char* src = (char*)test_d3d9_nv12_yuv;
    for (uint32_t i = 0; i < imageSize; i++)
    {
      std::memcpy(dst, src, imageSize);
      src += imageSize;
      dst += rect.Pitch;
    }

    for (uint32_t i = 0; i < imageSize / 2; i++)
    {
      std::memcpy(dst, src, imageSize);
      src += imageSize;
      dst += rect.Pitch;
    }
    nv12Surf->UnlockRect();
    status = m_device->StretchRect(nv12Surf.ptr(), nullptr, texSurf.ptr(), nullptr, D3DTEXF_LINEAR);
    m_device->SetTexture(0, texture.ptr());
  }

  void run() {
    this->adjustBackBuffer();

    m_device->BeginScene();

    m_device->Clear(
      0,
      nullptr,
      D3DCLEAR_TARGET,
      D3DCOLOR_RGBA(44, 62, 80, 0),
      0,
      0);

    m_device->Clear(
      0,
      nullptr,
      D3DCLEAR_ZBUFFER,
      0,
      0.5f,
      0);

    m_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

    m_device->EndScene();

    m_device->PresentEx(
      nullptr,
      nullptr,
      nullptr,
      nullptr,
      0);
  }

  void adjustBackBuffer() {
    RECT windowRect = { 0, 0, 1024, 600 };
    GetClientRect(m_window, &windowRect);

    Extent2D newSize = {
      static_cast<uint32_t>(windowRect.right - windowRect.left),
      static_cast<uint32_t>(windowRect.bottom - windowRect.top),
    };

    if (m_windowSize.w != newSize.w
     || m_windowSize.h != newSize.h) {
      m_windowSize = newSize;

      D3DPRESENT_PARAMETERS params;
      getPresentParams(params);
      HRESULT status = m_device->ResetEx(&params, nullptr);

      if (FAILED(status))
        throw DxvkError("Device reset failed");
    }
  }

  void getPresentParams(D3DPRESENT_PARAMETERS& params) {
    params.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
    params.BackBufferCount = 1;
    params.BackBufferFormat = D3DFMT_X8R8G8B8;
    params.BackBufferWidth = m_windowSize.w;
    params.BackBufferHeight = m_windowSize.h;
    params.EnableAutoDepthStencil = 0;
    params.Flags = 0;
    params.FullScreen_RefreshRateInHz = 0;
    params.hDeviceWindow = m_window;
    params.MultiSampleQuality = 0;
    params.MultiSampleType = D3DMULTISAMPLE_NONE;
    params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
    params.SwapEffect = D3DSWAPEFFECT_DISCARD;
    params.Windowed = TRUE;
  }

private:

  HWND                          m_window;
  Extent2D                      m_windowSize = { 1024, 600 };

  Com<IDirect3D9Ex>             m_d3d;
  Com<IDirect3DDevice9Ex>       m_device;

  Com<IDirect3DVertexShader9>   m_vs;
  Com<IDirect3DPixelShader9>    m_ps;
  Com<IDirect3DVertexBuffer9>   m_vb;
  Com<IDirect3DVertexDeclaration9> m_decl;

};

LRESULT CALLBACK WindowProc(HWND hWnd,
                            UINT message,
                            WPARAM wParam,
                            LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
  HWND hWnd;
  WNDCLASSEXW wc;
  ZeroMemory(&wc, sizeof(WNDCLASSEX));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
  wc.lpszClassName = L"WindowClass1";
  RegisterClassExW(&wc);

  hWnd = CreateWindowExW(0,
    L"WindowClass1",
    L"Our First Windowed Program",
    WS_OVERLAPPEDWINDOW,
    300, 300,
    640, 480,
    nullptr,
    nullptr,
    hInstance,
    nullptr);
  ShowWindow(hWnd, nCmdShow);

  MSG msg;

  try {
    TriangleApp app(hInstance, hWnd);

    while (true) {
      if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);

        if (msg.message == WM_QUIT)
          return msg.wParam;
      } else {
        app.run();
      }
    }
  } catch (const dxvk::DxvkError& e) {
    std::cerr << e.message() << std::endl;
    return msg.wParam;
  }
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  switch (message) {
    case WM_CLOSE:
      PostQuitMessage(0);
      return 0;
  }

  return DefWindowProc(hWnd, message, wParam, lParam);
}


code:
#include <cstring>

#include <d3d9.h>
#include <d3dcompiler.h>

#include "../test_utils.h"

using namespace dxvk;

struct Extent2D {
  uint32_t w, h;
};

const std::string g_vertexShaderCode = R"(

struct VS_INPUT {
  float3 Position : POSITION;
};

struct VS_OUTPUT {
  float4 Position : POSITION;
};

VS_OUTPUT main( VS_INPUT IN ) {
  VS_OUTPUT OUT;
  OUT.Position = float4(IN.Position, 0.6f);

  return OUT;
}

)";

const std::string g_pixelShaderCode = R"(

struct VS_OUTPUT {
  float4 Position : POSITION;
};

struct PS_OUTPUT {
  float4 Colour   : COLOR;
};

sampler g_texDepth : register( s0 );

PS_OUTPUT main( VS_OUTPUT IN ) {
  PS_OUTPUT OUT;

  OUT.Colour = tex2D(g_texDepth, float2(0, 0));
  OUT.Colour = 1.0;

  return OUT;
}

)";

Logger Logger::s_instance("triangle.log");

class TriangleApp {

public:

  TriangleApp(HINSTANCE instance, HWND window)
  : m_window(window) {
    HRESULT status = Direct3DCreate9Ex(D3D_SDK_VERSION, &m_d3d);

    if (FAILED(status))
      throw DxvkError("Failed to create D3D9 interface");

    UINT adapter = D3DADAPTER_DEFAULT;

    D3DADAPTER_IDENTIFIER9 adapterId;
    m_d3d->GetAdapterIdentifier(adapter, 0, &adapterId);

    Logger::info(str::format("Using adapter: ", adapterId.Description));

    auto CheckSRGBFormat = [&](D3DFORMAT fmt, const char* name) {
      HRESULT status = m_d3d->CheckDeviceFormat(adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, 0, D3DRTYPE_TEXTURE, fmt);
      Logger::warn(str::format("(linear) ", name, ": ", SUCCEEDED(status) ? "ok" : "nope"));

      status = m_d3d->CheckDeviceFormat(adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_QUERY_SRGBREAD, D3DRTYPE_TEXTURE, fmt);
      Logger::warn(str::format("(srgb) ", name, ": ", SUCCEEDED(status) ? "ok" : "nope"));
    };

    CheckSRGBFormat(D3DFMT_R5G6B5,       "R5G6B5");
    CheckSRGBFormat(D3DFMT_X1R5G5B5,     "X1R5G5B5");
    CheckSRGBFormat(D3DFMT_A1R5G5B5,     "A1R5G5B5");
    CheckSRGBFormat(D3DFMT_A4R4G4B4,     "A4R4G4B4");
    CheckSRGBFormat(D3DFMT_X4R4G4B4,     "X4R4G4B4");
    CheckSRGBFormat(D3DFMT_G16R16,       "G16R16");
    CheckSRGBFormat(D3DFMT_A2R10G10B10,  "A2R10G10B10");
    CheckSRGBFormat(D3DFMT_A16B16G16R16, "A16B16G16R16");

    //

    DWORD quality;
    status = m_d3d->CheckDepthStencilMatch(adapter, D3DDEVTYPE_HAL, D3DFMT_A8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_D24S8);
    status = m_d3d->CheckDeviceFormat(adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, 0, D3DRTYPE_SURFACE, D3DFMT_A8R8G8B8);
    status = m_d3d->CheckDeviceFormatConversion(adapter, D3DDEVTYPE_HAL, D3DFMT_A8R8G8B8, D3DFMT_A8R8G8B8);
    status = m_d3d->CheckDeviceMultiSampleType(adapter, D3DDEVTYPE_HAL, D3DFMT_A8R8G8B8, FALSE, D3DMULTISAMPLE_NONE, &quality);
    status = m_d3d->CheckDeviceMultiSampleType(adapter, D3DDEVTYPE_HAL, D3DFMT_A8R8G8B8, TRUE, D3DMULTISAMPLE_NONE, &quality);
    status = m_d3d->CheckDeviceType(adapter, D3DDEVTYPE_HAL, D3DFMT_A8R8G8B8, D3DFMT_A8R8G8B8, FALSE);
    status = m_d3d->CheckDeviceType(adapter, D3DDEVTYPE_HAL, D3DFMT_A8R8G8B8, D3DFMT_A8R8G8B8, TRUE);

    // NULL
    constexpr D3DFORMAT NullFormat = D3DFORMAT(MAKEFOURCC('N', 'U', 'L', 'L'));

    status = m_d3d->CheckDepthStencilMatch(adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, NullFormat, D3DFMT_D24S8);
    status = m_d3d->CheckDeviceFormat(adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, NullFormat);
    status = m_d3d->CheckDeviceFormat(adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, NullFormat);
    status = m_d3d->CheckDeviceFormatConversion(adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, NullFormat);
    status = m_d3d->CheckDeviceType(adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, NullFormat, false);
    status = m_d3d->CheckDeviceType(adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, NullFormat, true);
    //

    D3DPRESENT_PARAMETERS params;
    getPresentParams(params);

    status = m_d3d->CreateDeviceEx(
      adapter,
      D3DDEVTYPE_HAL,
      m_window,
      D3DCREATE_HARDWARE_VERTEXPROCESSING,
      &params,
      nullptr,
      &m_device);

    if (FAILED(status))
        throw DxvkError("Failed to create D3D9 device");

    // Funny Swapchain Refcounting
    // "One of the things COM does really well, is lifecycle management"
    // Implicit Swapchain
    {
      IDirect3DSurface9* pSurface1 = nullptr;
      IDirect3DSurface9* pSurface2 = nullptr;
      status = m_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pSurface1);
      D3DPRESENT_PARAMETERS newParams = params;
      newParams.BackBufferWidth  = 10;
      newParams.BackBufferHeight = 10;
      status = m_device->Reset(&newParams);
      status = m_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pSurface2);

      IDirect3DSwapChain9* pSwapChain2 = nullptr;
      IDirect3DSwapChain9* pSwapChain3 = nullptr;
      status = pSurface1->GetContainer(__uuidof(IDirect3DSwapChain9), reinterpret_cast<void**>(&pSwapChain2));
      status = pSurface2->GetContainer(__uuidof(IDirect3DSwapChain9), reinterpret_cast<void**>(&pSwapChain3));

      printf("E_NOINTERFACE! for pSwapchain2");
      status = m_device->Reset(&params);
    }
    // Additional swapchain
    {
      IDirect3DSwapChain9* pSwapChain2 = nullptr;
      IDirect3DSwapChain9* pSwapChain3 = nullptr;
      IDirect3DSwapChain9* pSwapChain4 = nullptr;
      IDirect3DSurface9* pSurface = nullptr;
      status = m_device->CreateAdditionalSwapChain(&params, &pSwapChain2);
      status = pSwapChain2->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pSurface);
      status = pSurface->GetContainer(__uuidof(IDirect3DSwapChain9), reinterpret_cast<void**>(&pSwapChain3));
      pSwapChain2->Release();
      UINT count = pSwapChain2->Release();
      printf("Count: %u - Should be 0 and swapchain dead!", count);
      status = pSurface->GetContainer(__uuidof(IDirect3DSwapChain9), reinterpret_cast<void**>(&pSwapChain4));
      // E_NOINTERFACE !
      printf("E_NOINTERFACE!");
    }

    m_device->AddRef();

    Com<IDirect3DSurface9> backbuffer;
    m_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);

    m_device->AddRef();

    Com<IDirect3DSwapChain9> swapchain;
    m_device->GetSwapChain(0, &swapchain);

    m_device->AddRef();

    DWORD bias = 0xDEADBEEF;
    status = m_device->GetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, &bias);
    status = m_device->SetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, MAKEFOURCC('G', 'E', 'T', '4'));
    status = m_device->GetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, &bias);
    status = m_device->SetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, MAKEFOURCC('G', 'E', 'T', '1'));
    status = m_device->GetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, &bias);

    // Vertex Shader
    {
      Com<ID3DBlob> blob;

      status = D3DCompile(
        g_vertexShaderCode.data(),
        g_vertexShaderCode.length(),
        nullptr, nullptr, nullptr,
        "main",
        "vs_2_0",
        0, 0, &blob,
        nullptr);

      if (FAILED(status))
        throw DxvkError("Failed to compile vertex shader");

      status = m_device->CreateVertexShader(reinterpret_cast<const DWORD*>(blob->GetBufferPointer()), &m_vs);

      if (FAILED(status))
        throw DxvkError("Failed to create vertex shader");
    }

    // Pixel Shader
    {
      Com<ID3DBlob> blob;

      status = D3DCompile(
        g_pixelShaderCode.data(),
        g_pixelShaderCode.length(),
        nullptr, nullptr, nullptr,
        "main",
        "ps_2_0",
        0, 0, &blob,
        nullptr);

      if (FAILED(status))
        throw DxvkError("Failed to compile pixel shader");

      status = m_device->CreatePixelShader(reinterpret_cast<const DWORD*>(blob->GetBufferPointer()), &m_ps);

      if (FAILED(status))
        throw DxvkError("Failed to create pixel shader");
    }

    m_device->SetVertexShader(m_vs.ptr());
    m_device->SetPixelShader(m_ps.ptr());

    m_device->AddRef();

    Com<IDirect3DSurface9> nullSurface;
    status = m_device->CreateRenderTarget(64, 64, D3DFORMAT(MAKEFOURCC('N', 'U', 'L', 'L')), D3DMULTISAMPLE_NONE, 0, FALSE, &nullSurface, nullptr);

    status = m_device->ColorFill(nullSurface.ptr(), nullptr, D3DCOLOR_RGBA(255, 0, 0, 255));

    Com<IDirect3DTexture9> defaultTexture;
    status = m_device->CreateTexture(64, 64, 1, 0, D3DFMT_DXT3, D3DPOOL_DEFAULT,   &defaultTexture, nullptr);

    m_device->AddRef();

    Com<IDirect3DSurface9> surface;
    status = defaultTexture->GetSurfaceLevel(0, &surface);

    m_device->AddRef();

    Com<IDirect3DTexture9> sysmemTexture;
    status = m_device->CreateTexture(64, 64, 1, 0, D3DFMT_DXT3, D3DPOOL_SYSTEMMEM, &sysmemTexture, nullptr);

    Com<IDirect3DSurface9> offscreenSurface;
    status = m_device->CreateOffscreenPlainSurfaceEx(64, 64, D3DFMT_DXT3, D3DPOOL_DEFAULT, &offscreenSurface, nullptr, 0);

    D3DLOCKED_RECT offscreenLock;
    status = offscreenSurface->LockRect(&offscreenLock, nullptr, 0);

    std::memset(offscreenLock.pBits, 0xFF, offscreenLock.Pitch * (64 / 4));

    status = offscreenSurface->UnlockRect();

    //status = m_device->ColorFill(offscreenSurface.ptr(), nullptr, D3DCOLOR_ARGB(255, 255, 0, 0));

    D3DLOCKED_RECT sysmemLock;
    status = sysmemTexture->LockRect(0, &sysmemLock, nullptr, 0);

    //D3DLOCKED_RECT offscreenLock;
    status = offscreenSurface->LockRect(&offscreenLock, nullptr, 0);

    std::memcpy(sysmemLock.pBits, offscreenLock.pBits, offscreenLock.Pitch * (64 / 4));

    sysmemTexture->UnlockRect(0);
    offscreenSurface->UnlockRect();

    status = m_device->UpdateTexture(sysmemTexture.ptr(), defaultTexture.ptr());

    status = m_device->SetTexture(0, defaultTexture.ptr());

    Com<IDirect3DSurface9> rt;
    status = m_device->CreateRenderTarget(1280, 720, D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &rt, nullptr);

    m_device->AddRef();

    Com<IDirect3DSurface9> rt2;
    status = m_device->CreateRenderTarget(1280, 720, D3DFMT_X8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &rt2, nullptr);

    m_device->AddRef();

    rt2 = nullptr;

    m_device->AddRef();

    RECT stretchRect1 = { 0, 0, 640, 720 };
    RECT stretchRect2 = { 640, 0, 1280, 720 };
    status = m_device->StretchRect(rt.ptr(), &stretchRect1, rt.ptr(), &stretchRect2, D3DTEXF_LINEAR);

    ///

    Com<IDirect3DSurface9> ds;
    //status = m_device->CreateDepthStencilSurface(1274, 695, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, FALSE, &ds, nullptr);
    status = m_device->CreateDepthStencilSurface(1280, 720, D3DFMT_D24X8, D3DMULTISAMPLE_NONE, 0, FALSE, &ds, nullptr);

    status = m_device->SetDepthStencilSurface(ds.ptr());
    status = m_device->SetRenderState(D3DRS_ZWRITEENABLE, 1);
    status = m_device->SetRenderState(D3DRS_ZENABLE, 1);
    status = m_device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);

    std::array<float, 9> vertices = {
      0.0f, 0.5f, 0.0f,
      0.5f, -0.5f, 0.0f,
      -0.5f, -0.5f, 0.0f,
    };

    const size_t vbSize = vertices.size() * sizeof(float);

    status = m_device->CreateVertexBuffer(vbSize, 0, 0, D3DPOOL_DEFAULT, &m_vb, nullptr);
    if (FAILED(status))
      throw DxvkError("Failed to create vertex buffer");

    void* data = nullptr;
    status = m_vb->Lock(0, 0, &data, 0);
    if (FAILED(status))
      throw DxvkError("Failed to lock vertex buffer");

    std::memcpy(data, vertices.data(), vbSize);

    status = m_vb->Unlock();
    if (FAILED(status))
      throw DxvkError("Failed to unlock vertex buffer");

    m_device->SetStreamSource(0, m_vb.ptr(), 0, 3 * sizeof(float));

    std::array<D3DVERTEXELEMENT9, 2> elements;

    elements[0].Method = 0;
    elements[0].Offset = 0;
    elements[0].Stream = 0;
    elements[0].Type = D3DDECLTYPE_FLOAT3;
    elements[0].Usage = D3DDECLUSAGE_POSITION;
    elements[0].UsageIndex = 0;

    elements[1] = D3DDECL_END();

    HRESULT result = m_device->CreateVertexDeclaration(elements.data(), &m_decl);
    if (FAILED(result))
      throw DxvkError("Failed to create vertex decl");

    m_device->SetVertexDeclaration(m_decl.ptr());

    ///

    Com<IDirect3DTexture9> myRT;
    status = m_device->CreateTexture(512, 256, 1, 0, D3DFMT_DXT1, D3DPOOL_DEFAULT, &myRT, nullptr);

    Com<IDirect3DSurface9> myRTSurf;
    myRT->GetSurfaceLevel(0, &myRTSurf);

    Com<IDirect3DTexture9> myCopyThing;
    status = m_device->CreateTexture(512, 256, 1, 0, D3DFMT_DXT1, D3DPOOL_DEFAULT, &myCopyThing, nullptr);

    Com<IDirect3DSurface9> myCopyThingSurf;
    myCopyThing->GetSurfaceLevel(0, &myCopyThingSurf);

    status = m_device->StretchRect(myRTSurf.ptr(), nullptr, myCopyThingSurf.ptr(), nullptr, D3DTEXF_NONE);

    D3DLOCKED_RECT rect;
    status = myCopyThing->LockRect(0, &rect, nullptr, D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK);

    m_device->SetRenderState(D3DRS_ALPHAREF, 256 + 255);
    m_device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_LESSEQUAL);
    m_device->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
  }

  void run() {
    this->adjustBackBuffer();

    m_device->BeginScene();

    m_device->Clear(
      0,
      nullptr,
      D3DCLEAR_TARGET,
      D3DCOLOR_RGBA(44, 62, 80, 0),
      0,
      0);

    m_device->Clear(
      0,
      nullptr,
      D3DCLEAR_ZBUFFER,
      0,
      0.5f,
      0);

    m_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

    m_device->EndScene();

    m_device->PresentEx(
      nullptr,
      nullptr,
      nullptr,
      nullptr,
      0);
  }

  void adjustBackBuffer() {
    RECT windowRect = { 0, 0, 1024, 600 };
    GetClientRect(m_window, &windowRect);

    Extent2D newSize = {
      static_cast<uint32_t>(windowRect.right - windowRect.left),
      static_cast<uint32_t>(windowRect.bottom - windowRect.top),
    };

    if (m_windowSize.w != newSize.w
     || m_windowSize.h != newSize.h) {
      m_windowSize = newSize;

      D3DPRESENT_PARAMETERS params;
      getPresentParams(params);
      HRESULT status = m_device->ResetEx(&params, nullptr);

      if (FAILED(status))
        throw DxvkError("Device reset failed");
    }
  }

  void getPresentParams(D3DPRESENT_PARAMETERS& params) {
    params.AutoDepthStencilFormat = D3DFMT_UNKNOWN;
    params.BackBufferCount = 1;
    params.BackBufferFormat = D3DFMT_X8R8G8B8;
    params.BackBufferWidth = m_windowSize.w;
    params.BackBufferHeight = m_windowSize.h;
    params.EnableAutoDepthStencil = 0;
    params.Flags = 0;
    params.FullScreen_RefreshRateInHz = 0;
    params.hDeviceWindow = m_window;
    params.MultiSampleQuality = 0;
    params.MultiSampleType = D3DMULTISAMPLE_NONE;
    params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
    params.SwapEffect = D3DSWAPEFFECT_DISCARD;
    params.Windowed = TRUE;
  }

private:

  HWND                          m_window;
  Extent2D                      m_windowSize = { 1024, 600 };

  Com<IDirect3D9Ex>             m_d3d;
  Com<IDirect3DDevice9Ex>       m_device;

  Com<IDirect3DVertexShader9>   m_vs;
  Com<IDirect3DPixelShader9>    m_ps;
  Com<IDirect3DVertexBuffer9>   m_vb;
  Com<IDirect3DVertexDeclaration9> m_decl;

};

LRESULT CALLBACK WindowProc(HWND hWnd,
                            UINT message,
                            WPARAM wParam,
                            LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
  HWND hWnd;
  WNDCLASSEXW wc;
  ZeroMemory(&wc, sizeof(WNDCLASSEX));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
  wc.lpszClassName = L"WindowClass1";
  RegisterClassExW(&wc);

  hWnd = CreateWindowExW(0,
    L"WindowClass1",
    L"Our First Windowed Program",
    WS_OVERLAPPEDWINDOW,
    300, 300,
    640, 480,
    nullptr,
    nullptr,
    hInstance,
    nullptr);
  ShowWindow(hWnd, nCmdShow);

  MSG msg;

  try {
    TriangleApp app(hInstance, hWnd);

    while (true) {
      if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);

        if (msg.message == WM_QUIT)
          return msg.wParam;
      } else {
        app.run();
      }
    }
  } catch (const dxvk::DxvkError& e) {
    std::cerr << e.message() << std::endl;
    return msg.wParam;
  }
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
  switch (message) {
    case WM_CLOSE:
      PostQuitMessage(0);
      return 0;
  }

  return DefWindowProc(hWnd, message, wParam, lParam);
}