/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2025-2026 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "d3d11_test.h"

RD_TEST(D3D11_Pixel_History, D3D11GraphicsTest)
{
  static constexpr const char *Description = "Tests pixel history";

  const std::string vertex = R"EOSHADER(

void main(float3 pos : POSITION, float4 col : COLOR0,
          out float4 outPos : SV_POSITION, out COL_TYPE outCol : COLOR0)
{
	outPos = float4(pos, 1.0f);
	outCol = ProcessColor(col);
}

)EOSHADER";

  const std::string pixel = R"EOSHADER(

void main(float4 pos : SV_POSITION, COL_TYPE col : COLOR0
#if COLOR == 1
  , out COL_TYPE outCol : SV_Target0
#endif
  )
{
  if (pos.x < DISCARD_X+1 && pos.x > DISCARD_X)
    discard;
  if (col.x > 10000 && col.y > 10000 && col.z > 10000)
    discard;
#if COLOR == 1
	outCol = col + COL_TYPE(0, 0, 0, ProcessColor((ALPHA_ADD).xxxx).x);
#endif
}

)EOSHADER";

  std::string mspixel = R"EOSHADER(

void main(float4 pos : SV_POSITION, uint sampleId : SV_SampleIndex
#if COLOR == 1
  , out COL_TYPE outCol : SV_Target0
#endif
  )
{
#if COLOR == 1
  float4 col = 0.0f.xxxx;
  if (sampleId == 0)
    col = float4(1, 0, 0, 1+ALPHA_ADD);
  else if (sampleId == 1)
    col = float4(0, 0, 1, 1+ALPHA_ADD);
  else if (sampleId == 2)
    col = float4(0, 1, 1, 1+ALPHA_ADD);
  else if (sampleId == 3)
    col = float4(1, 1, 1, 1+ALPHA_ADD);
  outCol = ProcessColor(col);
#endif
}

)EOSHADER";

  std::string comp = R"EOSHADER(

IMAGE_TYPE<COL_TYPE> Output : register(u0);

[numthreads(1, 1, 1)]
void main()
{
  float4 data = float4(3, 3, 3, 9);

  for(int x=0; x < 10; x++)
    for(int y=0; y < 10; y++)
      Output[STORE_TYPE(STORE_X+x, STORE_Y+y STORE_Z)] = ProcessColor(data);
}

)EOSHADER";

  struct D3D11StateSetup
  {
    struct SetupInfo
    {
      ID3D11VertexShaderPtr VS;
      ID3D11PixelShaderPtr PS;
      D3D11_DEPTH_STENCIL_DESC DepthStencilState;
      D3D11_RASTERIZER_DESC RasterizerState;
      D3D11_BLEND_DESC BlendState;
    };

    ID3D11VertexShaderPtr VS;
    ID3D11PixelShaderPtr PS;
    ID3D11DepthStencilStatePtr DS;
    ID3D11RasterizerStatePtr RS;
    ID3D11BlendStatePtr BS;
    UINT ref = 0x55;
    UINT SampleMask = ~0U;

    void init(ID3D11DevicePtr device, const SetupInfo &info)
    {
      VS = info.VS;
      PS = info.PS;
      device->CreateDepthStencilState(&info.DepthStencilState, &DS);
      device->CreateRasterizerState(&info.RasterizerState, &RS);
      device->CreateBlendState(&info.BlendState, &BS);
    }

    void set(ID3D11DeviceContextPtr context) const
    {
      context->VSSetShader(VS, NULL, 0);
      context->PSSetShader(PS, NULL, 0);
      context->RSSetState(RS);
      context->OMSetDepthStencilState(DS, ref);
      context->OMSetBlendState(BS, NULL, SampleMask);
    }
  };

  struct D3D11TestBatch
  {
    std::string name;

    bool uint = false;

    uint32_t mip = 0;

    ID3D11Texture2DPtr colImg2D;

    ID3D11RenderTargetViewPtr RTV;
    ID3D11DepthStencilViewPtr DSV;

    DXGI_FORMAT depthFormat;

    ID3D11UnorderedAccessViewPtr compUAV;
    ID3D11ComputeShaderPtr comp;

    uint32_t width, height;

    D3D11StateSetup depthWriteState;
    D3D11StateSetup scissorState;
    D3D11StateSetup stencilRefState;
    D3D11StateSetup stencilMaskState;
    D3D11StateSetup depthEqualState;
    D3D11StateSetup depthState;
    D3D11StateSetup stencilWriteState;
    D3D11StateSetup backgroundState;
    D3D11StateSetup noPsState;
    D3D11StateSetup noOutState;
    D3D11StateSetup basicState;
    D3D11StateSetup colorMaskState;
    D3D11StateSetup cullFrontState;
    D3D11StateSetup sampleColourState;
  };

  std::vector<D3D11TestBatch> batches;

  void BuildTestBatch(const std::string &name, uint32_t sampleCount, DXGI_FORMAT colourFormat,
                      DXGI_FORMAT depthStencilFormat, int targetMip = -1, int targetSlice = -1,
                      int targetDepthSlice = -1)
  {
    batches.push_back({});

    D3D11TestBatch &batch = batches.back();
    batch.name = name;

    batch.width = screenWidth;
    batch.height = screenHeight;

    uint32_t arraySize = targetSlice >= 0 ? 5 : 1;
    uint32_t mips = targetMip >= 0 ? 4 : 1;
    uint32_t depth = targetDepthSlice >= 0 ? 14 : 1;

    uint32_t mip = (uint32_t)std::max(0, targetMip);
    uint32_t slice = (uint32_t)std::max(0, targetSlice);
    if(depth > 1)
      slice = (uint32_t)targetDepthSlice;

    batch.mip = mip;

    std::string defines;
    defines +=
        "#define COLOR " + std::string(colourFormat == DXGI_FORMAT_UNKNOWN ? "0" : "1") + "\n";

    if(colourFormat == DXGI_FORMAT_R8G8B8A8_UINT || colourFormat == DXGI_FORMAT_R16G16B16A16_UINT ||
       colourFormat == DXGI_FORMAT_R32G32B32A32_UINT)
    {
      batch.uint = true;
      defines += R"(

#define COL_TYPE uint4
#define ALPHA_ADD 3

uint4 ProcessColor(float4 col)
{
  uint4 ret = uint4(16*col);

  if(col.x < 0.0f) ret.x = 0xfffffff0;
  if(col.y < 0.0f) ret.y = 0xfffffff0;
  if(col.z < 0.0f) ret.z = 0xfffffff0;


  return ret;
}

)";
    }
    else
    {
      defines += R"(

#define COL_TYPE float4
#define ALPHA_ADD 1.75

float4 ProcessColor(float4 col)
{
  float4 ret = col;

  // this obviously won't overflow F32 but it will overflow anything 16-bit and under
  if(col.x < 0.0f) ret.x = 100000.0f;
  if(col.y < 0.0f) ret.y = 100000.0f;
  if(col.z < 0.0f) ret.z = 100000.0f;

  return ret;
}

)";
    }

    defines += "#define DISCARD_X " + std::to_string(150 >> mip) + "\n";
    defines += "#define IMAGE_TYPE " +
               std::string(depth > 1       ? "RWTexture3D"
                           : arraySize > 1 ? "RWTexture2DArray"
                                           : "RWTexture2D") +
               "\n";
    defines += "#define STORE_X " + std::to_string(220 >> mip) + "\n";
    defines += "#define STORE_Y " + std::to_string(80 >> mip) + "\n";
    if(depth > 1 || arraySize > 1)
    {
      defines += "#define STORE_TYPE uint3\n";
      defines += "#define STORE_Z , " + std::to_string(slice) + "\n";
    }
    else
    {
      defines += "#define STORE_TYPE uint2\n";
      defines += "#define STORE_Z \n";
    }
    defines += "\n\n";

    ID3DBlobPtr vsBlob = Compile(defines + vertex, "main", "vs_5_0");
    ID3DBlobPtr psBlob = Compile(defines + pixel, "main", "ps_5_0");
    ID3DBlobPtr psMsaaBlob = Compile(defines + mspixel, "main", "ps_5_0");

    ID3DBlobPtr noOutPSBlob;

    if(colourFormat != DXGI_FORMAT_UNKNOWN)
    {
      defines += "#undef COLOR\n#define COLOR 0\n";
      noOutPSBlob = Compile(defines + pixel, "main", "ps_5_0");

      D3D11TextureCreator colCreator =
          MakeTexture(colourFormat, batch.width, batch.height, depth)
              .Mips(mips)
              // this is DepthOrArraySize so need to set it correctly from either
              .Array(arraySize)
              .Multisampled(sampleCount)
              .SRV()
              .RTV();

      if(sampleCount == 1)
        colCreator.UAV();

      if(depth > 1)
      {
        ID3D11Texture3DPtr colImg = colCreator;
        SetDebugName(colImg, batch.name + " colImg");

        batch.RTV = MakeRTV(colImg).FirstMip(mip).NumMips(1).FirstSlice(slice).NumSlices(1);

        batch.comp = CreateCS(Compile(defines + comp, "main", "cs_5_0"));
        SetDebugName(batch.comp, batch.name + " comp");
        batch.compUAV = MakeUAV(colImg).FirstMip(mip);
        SetDebugName(batch.compUAV, batch.name + " compUAV");
      }
      else
      {
        batch.colImg2D = colCreator;
        SetDebugName(batch.colImg2D, batch.name + " colImg");

        batch.RTV = MakeRTV(batch.colImg2D).FirstMip(mip).NumMips(1).FirstSlice(slice).NumSlices(1);

        if(sampleCount == 1)
        {
          batch.comp = CreateCS(Compile(defines + comp, "main", "cs_5_0"));
          SetDebugName(batch.comp, batch.name + " comp");
          batch.compUAV = MakeUAV(batch.colImg2D).FirstMip(mip);
          SetDebugName(batch.compUAV, batch.name + " compUAV");
        }
      }
    }

    batch.depthFormat = depthStencilFormat;

    if(depthStencilFormat != DXGI_FORMAT_UNKNOWN)
    {
      // can't create 2D views of 3D with depth images, so we just use a normal 2D array image
      if(depth > 1)
      {
        arraySize = depth;
        depth = 1;
      }

      ID3D11Texture2DPtr depthImg =
          MakeTexture(depthStencilFormat, batch.width, batch.height, depth)
              .Mips(mips)
              // this is DepthOrArraySize so need to set it correctly from either
              .Array(arraySize)
              .Multisampled(sampleCount)
              .DSV();
      SetDebugName(depthImg, batch.name + " depthImg");

      batch.DSV = MakeDSV(depthImg).FirstMip(mip).NumMips(1).FirstSlice(slice).NumSlices(1);
    }

    batch.width >>= mip;
    batch.height >>= mip;

    D3D11StateSetup::SetupInfo stateInfo = {};

    ID3D11VertexShaderPtr VS = CreateVS(vsBlob);
    SetDebugName(VS, batch.name + " NormalVS");
    stateInfo.VS = VS;

    ID3D11PixelShaderPtr PS = CreatePS(psBlob);
    SetDebugName(PS, batch.name + " NormalPS");
    stateInfo.PS = PS;

    ID3D11PixelShaderPtr noOutPS;

    if(noOutPSBlob)
    {
      noOutPS = CreatePS(noOutPSBlob);
      SetDebugName(noOutPS, batch.name + " NoOutPS");
    }

    stateInfo.BlendState.RenderTarget[0].RenderTargetWriteMask = 0xf;

    stateInfo.RasterizerState.FillMode = D3D11_FILL_SOLID;
    stateInfo.RasterizerState.MultisampleEnable = sampleCount > 1;
    stateInfo.RasterizerState.ScissorEnable = TRUE;
    stateInfo.RasterizerState.DepthClipEnable = TRUE;
    stateInfo.RasterizerState.CullMode = D3D11_CULL_BACK;

    stateInfo.DepthStencilState.DepthEnable = TRUE;
    stateInfo.DepthStencilState.DepthFunc = D3D11_COMPARISON_ALWAYS;
    stateInfo.DepthStencilState.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
    stateInfo.DepthStencilState.StencilEnable = FALSE;
    stateInfo.DepthStencilState.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
    stateInfo.DepthStencilState.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    stateInfo.DepthStencilState.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
    stateInfo.DepthStencilState.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE;
    stateInfo.DepthStencilState.StencilReadMask = 0xff;
    stateInfo.DepthStencilState.StencilWriteMask = 0xff;
    stateInfo.DepthStencilState.BackFace = stateInfo.DepthStencilState.FrontFace;

    stateInfo.DepthStencilState.DepthFunc = D3D11_COMPARISON_ALWAYS;
    batch.depthWriteState.init(dev, stateInfo);

    {
      D3D11StateSetup::SetupInfo depthEqualStateInfo = stateInfo;
      depthEqualStateInfo.DepthStencilState.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
      depthEqualStateInfo.DepthStencilState.DepthFunc = D3D11_COMPARISON_EQUAL;

      batch.depthEqualState.init(dev, depthEqualStateInfo);
    }

    {
      D3D11StateSetup::SetupInfo scissorStencilStates = stateInfo;
      scissorStencilStates.DepthStencilState.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
      scissorStencilStates.DepthStencilState.DepthEnable = FALSE;

      batch.scissorState.init(dev, scissorStencilStates);

      batch.stencilRefState.init(dev, scissorStencilStates);
      batch.stencilRefState.ref = 0x67;

      scissorStencilStates.DepthStencilState.StencilReadMask = 0;
      scissorStencilStates.DepthStencilState.StencilWriteMask = 0;

      batch.stencilMaskState.init(dev, scissorStencilStates);
    }

    stateInfo.DepthStencilState.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;

    batch.depthState.init(dev, stateInfo);

    stateInfo.DepthStencilState.StencilEnable = TRUE;
    batch.stencilWriteState.init(dev, stateInfo);

    stateInfo.DepthStencilState.StencilEnable = FALSE;
    batch.backgroundState.init(dev, stateInfo);

    {
      stateInfo.PS = NULL;
      stateInfo.DepthStencilState.StencilEnable = TRUE;
      batch.noPsState.init(dev, stateInfo);
      batch.noPsState.ref = 0x33;
      stateInfo.PS = PS;
    }

    if(noOutPS)
    {
      stateInfo.PS = noOutPS;
      stateInfo.DepthStencilState.StencilEnable = FALSE;
      batch.noOutState.init(dev, stateInfo);
      stateInfo.PS = PS;
    }

    stateInfo.DepthStencilState.StencilEnable = TRUE;
    stateInfo.DepthStencilState.FrontFace.StencilFunc = D3D11_COMPARISON_GREATER;
    batch.basicState.init(dev, stateInfo);
    stateInfo.DepthStencilState.StencilEnable = FALSE;

    {
      D3D11StateSetup::SetupInfo maskStateInfo = stateInfo;
      maskStateInfo.BlendState.RenderTarget[0].RenderTargetWriteMask = 0x3;
      batch.colorMaskState.init(dev, maskStateInfo);
    }

    stateInfo.RasterizerState.CullMode = D3D11_CULL_FRONT;
    batch.cullFrontState.init(dev, stateInfo);
    stateInfo.RasterizerState.CullMode = D3D11_CULL_BACK;

    stateInfo.PS = CreatePS(psMsaaBlob);
    SetDebugName(stateInfo.PS, batch.name + " MSAAPS");

    stateInfo.DepthStencilState.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
    batch.sampleColourState.init(dev, stateInfo);
    if(sampleCount == 4)
      batch.sampleColourState.SampleMask = 0x7;
  }

  void RunDraw(const PixelHistory::draw &draw, uint32_t numInstances = 1)
  {
    ctx->DrawInstanced(draw.count, numInstances, draw.first, 0);
  }

  void RunBatch(const D3D11TestBatch &b)
  {
    float factor = float(b.width) / float(screenWidth);

    D3D11_VIEWPORT v = {};
    v.Width = (float)b.width - (20.0f * factor);
    v.Height = (float)b.height - (20.0f * factor);
    v.TopLeftX = floorf(10.0f * factor);
    v.TopLeftY = floorf(10.0f * factor);
    v.MaxDepth = 1.0f;

    D3D11_RECT scissor = {0, 0, (LONG)b.width, (LONG)b.height};
    D3D11_RECT scissorPass = {95 >> b.mip, 245 >> b.mip, 115U >> b.mip, 253U >> b.mip};
    D3D11_RECT scissorFail = {95 >> b.mip, 245 >> b.mip, 103U >> b.mip, 253U >> b.mip};

    ctx->RSSetViewports(1, &v);
    ctx->RSSetScissorRects(1, &scissor);

    ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    ctx->IASetInputLayout(defaultLayout);

    // draw the setup triangles

    pushMarker("Setup");
    {
      setMarker("Depth Write");
      b.depthWriteState.set(ctx);
      RunDraw(PixelHistory::DepthWrite);

      setMarker("Depth Equal Setup");
      RunDraw(PixelHistory::DepthEqualSetup);

      setMarker("Unbound Shader");
      b.noPsState.set(ctx);
      RunDraw(PixelHistory::UnboundPS);

      setMarker("Stencil Write");
      b.stencilWriteState.set(ctx);
      RunDraw(PixelHistory::StencilWrite);

      setMarker("Background");
      b.backgroundState.set(ctx);
      RunDraw(PixelHistory::Background);

      setMarker("Cull Front");
      b.cullFrontState.set(ctx);
      RunDraw(PixelHistory::CullFront);

      // depth bounds would be here but D3D11 does not support it
    }
    popMarker();

    pushMarker("Stress Test");
    {
      pushMarker("Lots of Drawcalls");
      {
        setMarker("300 Draws");
        b.depthWriteState.set(ctx);
        for(int d = 0; d < 300; ++d)
          RunDraw(PixelHistory::Draws300);
      }
      popMarker();

      setMarker("300 Instances");
      RunDraw(PixelHistory::Instances300, 300);
    }
    popMarker();

    setMarker("Simple Test");
    b.basicState.set(ctx);
    RunDraw(PixelHistory::MainTest);

    b.scissorState.set(ctx);

    setMarker("Scissor Fail");
    ctx->RSSetScissorRects(1, &scissorFail);
    RunDraw(PixelHistory::ScissorFail);

    setMarker("Scissor Pass");
    ctx->RSSetScissorRects(1, &scissorPass);
    RunDraw(PixelHistory::ScissorPass);

    ctx->RSSetScissorRects(1, &scissor);

    setMarker("Stencil Ref");
    b.stencilRefState.set(ctx);
    RunDraw(PixelHistory::StencilRef);

    setMarker("Stencil Mask");
    b.stencilMaskState.set(ctx);
    RunDraw(PixelHistory::StencilMask);

    setMarker("Depth Test");
    b.depthState.set(ctx);
    RunDraw(PixelHistory::DepthTest);

    setMarker("Sample Colouring");
    b.sampleColourState.set(ctx);
    RunDraw(PixelHistory::SampleColour);

    setMarker("Depth Equal Fail");
    b.depthEqualState.set(ctx);
    RunDraw(PixelHistory::DepthEqualFail);

    setMarker("Depth Equal Pass");
    b.depthEqualState.set(ctx);
    if(b.depthFormat == DXGI_FORMAT_D24_UNORM_S8_UINT)
      RunDraw(PixelHistory::DepthEqualPass24);
    else if(b.depthFormat == DXGI_FORMAT_D16_UNORM)
      RunDraw(PixelHistory::DepthEqualPass16);
    else
      RunDraw(PixelHistory::DepthEqualPass32);

    setMarker("Colour Masked");
    b.colorMaskState.set(ctx);
    RunDraw(PixelHistory::ColourMask);

    setMarker("Overflowing");
    b.backgroundState.set(ctx);
    RunDraw(PixelHistory::OverflowingDraw);

    setMarker("Per-Fragment discarding");
    b.backgroundState.set(ctx);
    RunDraw(PixelHistory::PerFragDiscard);

    if(b.noOutState.VS)
    {
      setMarker("No Output Shader");
      b.noOutState.set(ctx);
      RunDraw(PixelHistory::UnboundPS);
    }
  }

  int main()
  {
    // initialise, create window, create device, etc
    if(!Init())
      return 3;

    PixelHistory::init();

    ID3D11BufferPtr vb = MakeBuffer()
                             .Vertex()
                             .Size(UINT(sizeof(DefaultA2V) * PixelHistory::vb.size()))
                             .Data(PixelHistory::vb.data());

    ID3D11Texture2DPtr bbBlitSource;

    BuildTestBatch("Basic", 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
    bbBlitSource = batches.back().colImg2D;

    BuildTestBatch("MSAA", 4, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
    BuildTestBatch("Colour Only", 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN);
    BuildTestBatch("Depth Only", 1, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
    BuildTestBatch("Mip & Slice", 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
                   2, 3);
    BuildTestBatch("Slice MSAA", 4, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_D32_FLOAT_S8X24_UINT,
                   -1, 3);

    // can't test 3D textures with depth as 3D depth is not supported and can't mix 3D with 2D array as in other APIs
    BuildTestBatch("3D texture", 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, -1, -1, 8);

    BuildTestBatch("D24S8", 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_D24_UNORM_S8_UINT);

    BuildTestBatch("D16", 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_D16_UNORM);
    BuildTestBatch("D32", 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_D32_FLOAT);

    BuildTestBatch("F16 UNORM", 1, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
    BuildTestBatch("F16 FLOAT", 1, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
    BuildTestBatch("F32 FLOAT", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, DXGI_FORMAT_D32_FLOAT_S8X24_UINT);

    BuildTestBatch("8-bit uint", 1, DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
    BuildTestBatch("16-bit uint", 1, DXGI_FORMAT_R16G16B16A16_UINT, DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
    BuildTestBatch("32-bit uint", 1, DXGI_FORMAT_R32G32B32A32_UINT, DXGI_FORMAT_D32_FLOAT_S8X24_UINT);

    while(Running())
    {
      for(const D3D11TestBatch &b : batches)
      {
        IASetVertexBuffer(vb, sizeof(DefaultA2V), 0);

        {
          pushMarker("Batch: " + b.name);
          {
            setMarker("Begin RenderPass");

            float clearColor[4] = {0.2f, 0.2f, 0.2f, 1.0f};
            if(b.uint)
            {
              clearColor[0] = 80.0f;
              clearColor[1] = 80.0f;
              clearColor[2] = 80.0f;
              clearColor[3] = 16.0f;
            }

            if(b.RTV)
              ctx->ClearRenderTargetView(b.RTV, clearColor);
            if(b.DSV)
              ctx->ClearDepthStencilView(b.DSV, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

            ID3D11RenderTargetViewPtr RTV = b.RTV;
            ctx->OMSetRenderTargets(b.RTV ? 1 : 0, &RTV.GetInterfacePtr(), b.DSV);

            RunBatch(b);

            if(b.comp)
            {
              setMarker("Compute write");

              ctx->OMSetRenderTargets(0, NULL, NULL);
              ctx->CSSetShader(b.comp, NULL, 0);
              ID3D11UnorderedAccessViewPtr UAV = b.compUAV;
              ctx->CSSetUnorderedAccessViews(0, 1, &UAV.GetInterfacePtr(), NULL);
              ctx->Dispatch(1, 1, 1);
            }
          }
          popMarker();
        }
      }

      if(bbBlitSource)
      {
        ctx->OMSetBlendState(NULL, NULL, ~0U);
        blitToSwap(bbBlitSource);
      }

      Present();
    }

    return 0;
  }
};

REGISTER_TEST();
