// Global tables generated by the metadesk code in Ryan's codebase,
// here I do them by hand, for now

//////////////////////////////////
//~ Input element descriptions

global D3D11_INPUT_ELEMENT_DESC r_d3d11_g_rect2d_input_layout_elems[] =
{
  {"POS", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 
    0, D3D11_INPUT_PER_INSTANCE_DATA, 1},
  {"TEX", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 
    D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1},
  {"COL", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 
    D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1},
  {"COL", 1, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 
    D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1},
  {"COL", 2, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 
    D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1},
  {"COL", 3, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 
    D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1},
  { "CRAD", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 
    D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
  {"STY", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 
    D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_INSTANCE_DATA, 1},
};



global R_D3D11_CmdGlobalKindInfo r_d3d11_g_cmd_global_kind_info_table[R_D3D11_CmdGlobalKind_COUNT] = 
{
  {0}, // Nil
  {sizeof(R_D3D11_CmdGlobals_Rect2D)},
};


R_D3D11_ShaderPairKindInfo r_d3d11_g_shader_pair_kind_info_table[R_D3D11_ShaderPairKind_COUNT] = {0};

global R_D3D11_State *r_d3d11_state;



//////////////////////////////////
//~ Render helpers

// Create a buffer holding all the instance data for a current batch
r_function ID3D11Buffer *
R_D3D11_instance_buffer_from_batch_list(R_BatchList *list)
{
  U64 needed_size = list->byte_count;
  if(needed_size > Kilobytes(64)) 
  {
    break_debugger();
  }

  ID3D11Buffer *buffer = r_d3d11_state->scratch_buffer_64kb;
  
  D3D11_MAPPED_SUBRESOURCE sub_resource = {0};
  ID3D11DeviceContext_Map(r_d3d11_state->base_device_context, (ID3D11Resource *)buffer, 0,
      D3D11_MAP_WRITE_DISCARD, 0, &sub_resource);
  U8 *ptr = (U8 *)sub_resource.pData;
  for(R_Batch *batch = list->first; batch != 0; batch = batch->next)
  {
    MemoryCopy(ptr, batch->v, batch->byte_count);
    ptr += batch->byte_count;
  }
  ID3D11DeviceContext_Unmap(r_d3d11_state->base_device_context, (ID3D11Resource *)buffer, 0);

      

  return buffer;
}


// Temporary initialisation of the shader table, probably we just keep reading the shader src from file here
// in the end, and we generate a table through metaprogramming.
r_function void
R_D3D11_initialise_shader_table(Arena* shader_src_arena)
{
  String8 shader_paths[R_D3D11_ShaderPairKind_COUNT] = {0};
  shader_paths[R_D3D11_ShaderPairKind_Rect2D] = 
    str8_lit("D:\\dev\\app_codebase\\src\\render\\d3d11\\shaders\\rect2d.hlsl");
  
  
  
  String8 shader_name[R_D3D11_ShaderPairKind_COUNT] = {0};
  shader_name[R_D3D11_ShaderPairKind_Rect2D] = str8_lit("r_d3d11_g_rect2d_shader_src");


  for(R_D3D11_ShaderPairKind kind = (R_D3D11_ShaderPairKind)(R_D3D11_ShaderPairKind_Nil+1);
      kind < R_D3D11_ShaderPairKind_COUNT;
      kind = (R_D3D11_ShaderPairKind)(kind+1))
  {
    // We're on windows anyway so might as well  
    OS_Handle file = OS_file_open(OS_AccessFlag_Read, shader_paths[kind]); 
    OS_FileAttributes attrs = OS_attributes_from_file(file);
    String8 shader_src = OS_file_read(shader_src_arena, file, 0, attrs.size);
    r_d3d11_g_shader_pair_kind_info_table[kind].name = shader_name[kind];
    r_d3d11_g_shader_pair_kind_info_table[kind].shader_blob = shader_src;
    if(kind == R_D3D11_ShaderPairKind_Rect2D) {
      r_d3d11_g_shader_pair_kind_info_table[kind].element_description = r_d3d11_g_rect2d_input_layout_elems; 
      r_d3d11_g_shader_pair_kind_info_table[kind].element_description_count = 
        ArrayCount(r_d3d11_g_rect2d_input_layout_elems);
    } 
    OS_file_close(file);
  }
}

r_function DXGI_FORMAT
R_D3D11_DXGI_format_from_tex2d_format(R_Tex2DFormat format)
{
  DXGI_FORMAT result = DXGI_FORMAT_R8G8B8A8_UNORM;
  switch(format)
  {
    case R_Tex2DFormat_R8: { result = DXGI_FORMAT_R8_UNORM; } break;
    default:
    case R_Tex2DFormat_RGBA8: {} break;
  }
  
  return result;
}

r_function R_D3D11_Tex2D
R_D3D11_tex2d_from_handle(R_Handle handle) 
{
  R_D3D11_Tex2D tex = {0};
  tex.texture = (ID3D11Texture2D *)handle.u64[0];
  tex.view    = (ID3D11ShaderResourceView *)handle.u64[1];
  tex.size.x  = handle.u32[4]; 
  tex.size.y  = handle.u32[5];
  tex.format  = (R_Tex2DFormat)handle.u32[6];
  tex.kind    = (R_Tex2DKind)handle.u32[7];
  return tex;
}

r_function R_Handle 
R_D3D11_handle_from_tex2d(R_D3D11_Tex2D texture)
{
  R_Handle result = {0};
  result.u64[0] = (U64)texture.texture;
  result.u64[1] = (U64)texture.view;
  result.u32[4] = texture.size.x;
  result.u32[5] = texture.size.y;
  result.u32[6] = texture.format;
  result.u32[7] = texture.kind;
  return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
//~ Backend hooks, ie implementations of the render backend abstraction (render_core.h)
//

// This initialises the D3D11 layer. We have a big struct that holds all the state.
// How this is done in RJFs codebase really looks like the mmozeiko example but with the big
// global struct holding the state. The Hidden Grove (rjf) uses the C++ style though, and 
// also the d3d11_1 header which extends d3d11 in some way... But I will reproduce mmozeiko's C example here.
r_function R_InitReceipt 
R_init(OS_InitReceipt os_init, OS_InitGfxReceipt os_gfx_init) 
{

  if(is_main_thread() && r_d3d11_state == 0)
  {
    Arena *arena = m_make_arena_reserve(Gigabytes(16));
    r_d3d11_state = PushArray(arena, R_D3D11_State, 1);
    r_d3d11_state->arena = arena;
  
    HRESULT hr;

    // Create D3D11 device & context
    UINT flags = 0;
#if BUILD_DEBUG
    flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
    D3D_FEATURE_LEVEL feature_levels[] = { D3D_FEATURE_LEVEL_11_0 };
    hr = D3D11CreateDevice(
        0, // Primary adapter chosen automatically if null
        D3D_DRIVER_TYPE_HARDWARE,
        0, // null for non-softweare driver types
        flags,
        feature_levels,
        ArrayCount(feature_levels),
        D3D11_SDK_VERSION,
        &r_d3d11_state->base_device,
        0,
        &r_d3d11_state->base_device_context
        );
    AssertHR(hr);

    // Enable useful debug messages and breaks
#if BUILD_DEBUG
    // for debug builds enable VERY USEFUL debug break on API errors
    {
        ID3D11InfoQueue* info;
        ID3D11Device_QueryInterface(r_d3d11_state->base_device, &IID_ID3D11InfoQueue, (void**)&info);
        ID3D11InfoQueue_SetBreakOnSeverity(info, D3D11_MESSAGE_SEVERITY_CORRUPTION, TRUE);
        ID3D11InfoQueue_SetBreakOnSeverity(info, D3D11_MESSAGE_SEVERITY_ERROR, TRUE);
        ID3D11InfoQueue_Release(info);
    }

    // enable debug break for DXGI too
    {
        IDXGIInfoQueue* dxgiInfo;
        hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void**)&dxgiInfo);
        AssertHR(hr);
        IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfo, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE);
        IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfo, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE);
        IDXGIInfoQueue_Release(dxgiInfo);
    }

    // NOTE(anton): from mmozeiko's gist:
    // "after this there's no need to check for any errors on device functions manually
    // so all HRESULT return  values in this code will be ignored
    // debugger will break on errors anyway"
    // TODO(anton): Remove AssertHR and use of hr below to check this.
#endif


    // Create objects for swapchain-creation (dxgi)
    {
      hr = ID3D11Device_QueryInterface(r_d3d11_state->base_device, 
          &IID_IDXGIDevice, (void **)&r_d3d11_state->dxgi_device);
      AssertHR(hr);

      hr = IDXGIDevice_GetAdapter(r_d3d11_state->dxgi_device, &r_d3d11_state->dxgi_adapter);
      AssertHR(hr);

      hr = IDXGIAdapter_GetParent(r_d3d11_state->dxgi_adapter, &IID_IDXGIFactory2, 
          (void **)&r_d3d11_state->dxgi_factory2);
      AssertHR(hr);

    }
  }

  //- Set up initial pipeline state
  {
    D3D11_RASTERIZER_DESC desc = 
    {
      .FillMode = D3D11_FILL_SOLID,
      .CullMode = D3D11_CULL_NONE,
      .DepthClipEnable = TRUE,
    };
    ID3D11Device_CreateRasterizerState(r_d3d11_state->base_device, &desc, &r_d3d11_state->rasterizer_state);
  }
  {
    {
      D3D11_SAMPLER_DESC desc = {0};
      {
        desc.Filter         = D3D11_FILTER_MIN_MAG_MIP_POINT;
        desc.AddressU       = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.AddressV       = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.AddressW       = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
      }
      //r_d3d11_state->device->CreateSamplerState(&desc, &r_d3d11_state->nearest_sampler);
      ID3D11Device_CreateSamplerState(r_d3d11_state->base_device, &desc, &r_d3d11_state->nearest_sampler);
    }
    {
      D3D11_SAMPLER_DESC desc = {0};
      {
        desc.Filter         = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
        desc.AddressU       = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.AddressV       = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.AddressW       = D3D11_TEXTURE_ADDRESS_WRAP;
        desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
      }
      //r_d3d11_state->device->CreateSamplerState(&desc, &r_d3d11_state->linear_sampler);
      ID3D11Device_CreateSamplerState(r_d3d11_state->base_device, &desc, &r_d3d11_state->linear_sampler); 
    }
  }
  {
    D3D11_BLEND_DESC desc =
    {
      .RenderTarget[0] =
      {
        .BlendEnable = TRUE,
        .SrcBlend = D3D11_BLEND_SRC_ALPHA,
        .DestBlend = D3D11_BLEND_INV_SRC_ALPHA,
        .BlendOp = D3D11_BLEND_OP_ADD,
        .SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA,
        .DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA,
        .BlendOpAlpha = D3D11_BLEND_OP_ADD,
        .RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL,
      },
    };
    ID3D11Device_CreateBlendState(r_d3d11_state->base_device, &desc, &r_d3d11_state->main_blend_state);
  }

  {
    D3D11_DEPTH_STENCIL_DESC desc = 
    {
      .DepthEnable = FALSE,
      .DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL,
      .DepthFunc = D3D11_COMPARISON_LESS,
      .StencilEnable = FALSE,
      .StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK,
      .StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK
    };
    ID3D11Device_CreateDepthStencilState(r_d3d11_state->base_device, &desc, 
        &r_d3d11_state->depth_stencil_state);
  }

  //- Global command buffer creation
  // Loop over the kind enums that are also the indices into the tables
  for(R_D3D11_CmdGlobalKind kind = (R_D3D11_CmdGlobalKind)(R_D3D11_CmdGlobalKind_Nil+1);
      kind < R_D3D11_CmdGlobalKind_COUNT;
      kind = (R_D3D11_CmdGlobalKind)(kind + 1))
  {
    D3D11_BUFFER_DESC desc = {0}; 
    // The ByteWidth of the buffer is the size of the CmdGlobalKind struct as defined in the table.
    // Then we align it to 16 bytes by adding 15 and subtracting the size mod 16.
    desc.ByteWidth = r_d3d11_g_cmd_global_kind_info_table[kind].size;
    desc.ByteWidth += 15;
    desc.ByteWidth -= desc.ByteWidth % 16;
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    ID3D11Device_CreateBuffer(r_d3d11_state->base_device, &desc, 0 /* subresource data*/, 
        &r_d3d11_state->cmd_global_buffer_table[kind]);
  }

  // Temporary hold the source code of the shaders so we can compile them
  Arena* shader_src_arena = m_make_arena_reserve(Megabytes(8));
  R_D3D11_initialise_shader_table(shader_src_arena);

  //- Create shader objects
  for(R_D3D11_ShaderPairKind kind = (R_D3D11_ShaderPairKind)(R_D3D11_ShaderPairKind_Nil+1);
      kind < R_D3D11_ShaderPairKind_COUNT;
      kind = (R_D3D11_ShaderPairKind)(kind + 1)) 
  {
    R_D3D11_ShaderPairKindInfo *info = &r_d3d11_g_shader_pair_kind_info_table[kind];

    // Compile vertex shader
    ID3DBlob *vs_src_blob = 0;
    ID3DBlob *vs_src_errors = 0;
    ID3D11VertexShader *vs = 0;
    String8 vs_errors = {0};
    
    HRESULT hr;
    {
      hr = D3DCompile(info->shader_blob.str, info->shader_blob.size, (char *)info->name.str, 0, 0, 
          "vs_main", "vs_5_0", 0 /* flags */, 0, &vs_src_blob, &vs_src_errors);
      if(vs_src_errors) 
      {
        vs_errors = str8( (U8 *)ID3D10Blob_GetBufferPointer(vs_src_errors), 
            (U64)ID3D10Blob_GetBufferSize(vs_src_errors));
        break_debugger();
      }
      else
      {
        ID3D11Device_CreateVertexShader(r_d3d11_state->base_device, ID3D10Blob_GetBufferPointer(vs_src_blob),
            ID3D10Blob_GetBufferSize(vs_src_blob), 0, &vs);
      }
    }

    // Make input layout
    ID3D11InputLayout *input_layout = 0;
    if(info->element_description != 0) 
    {
      ID3D11Device_CreateInputLayout(r_d3d11_state->base_device, info->element_description,
          info->element_description_count,
          ID3D10Blob_GetBufferPointer(vs_src_blob), ID3D10Blob_GetBufferSize(vs_src_blob),
          &input_layout);
    }

    // Compile pixel shader
    ID3DBlob *ps_src_blob = 0;
    ID3DBlob *ps_src_errors = 0;
    ID3D11PixelShader *ps = 0;
    String8 ps_errors = {0};
    {
      hr = D3DCompile(info->shader_blob.str, info->shader_blob.size, (char *)info->name.str, 0, 0,
          "ps_main", "ps_5_0", 0, 0, &ps_src_blob, &ps_src_errors);
      if(ps_src_errors) 
      {
        ps_errors = str8( (U8 *)ID3D10Blob_GetBufferPointer(ps_src_errors), 
            (U64)ID3D10Blob_GetBufferSize(ps_src_errors));
        break_debugger();
      }
      else
      {
        ID3D11Device_CreatePixelShader(r_d3d11_state->base_device, ID3D10Blob_GetBufferPointer(ps_src_blob),
            ID3D10Blob_GetBufferSize(ps_src_blob), 0, &ps);
      }
    }

    // Store in state
    r_d3d11_state->input_layout_table[kind] = input_layout;
    r_d3d11_state->vs_table[kind] = vs;
    r_d3d11_state->ps_table[kind] = ps;

    ID3D10Blob_Release(vs_src_blob);
    ID3D10Blob_Release(ps_src_blob);
  }
  m_arena_release(shader_src_arena);

  //- Scratch buffer resources
  // 64k
  {
    D3D11_BUFFER_DESC desc = {0};
    {
      desc.ByteWidth      = Kilobytes(64);
      desc.Usage          = D3D11_USAGE_DYNAMIC;
      desc.BindFlags      = D3D11_BIND_VERTEX_BUFFER;
      desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    }
    ID3D11Device_CreateBuffer(r_d3d11_state->base_device, &desc, 0, &r_d3d11_state->scratch_buffer_64kb);
  }

  R_InitReceipt out = {0};
  return out;
}


// This creates the swapchain and attaches it to the OS handle.
// Since we want to be able to do this dynamically we will store the necessary creation objects in
// the D3D11 state struct (dxgi device, adapter, factory2).
  r_function R_Handle
R_window_equip(OS_Handle window_handle)
{
  // We are just doing regular OS alloc for this, ie no arena.
  R_D3D11_WindowEquip *equip = (R_D3D11_WindowEquip *)OS_reserve(sizeof(R_D3D11_WindowEquip));
  OS_commit(equip, sizeof(*equip));
  OS_W32_Window *window = (OS_W32_Window *)window_handle.u64[0];
  HWND hwnd = window->hwnd;
  DXGI_SWAP_CHAIN_DESC1 swapchain_desc = 
  {
    // default 0 value for width & height means to get it from HWND automatically
    //.Width = 0,
    //.Height = 0,

    // or use DXGI_FORMAT_R8G8B8A8_UNORM_SRGB for storing sRGB
    .Format = DXGI_FORMAT_R8G8B8A8_UNORM,

    // FLIP presentation model does not allow MSAA framebuffer
    // if you want MSAA then you'll need to render offscreen and manually
    // resolve to non-MSAA framebuffer
    .SampleDesc = { 1, 0 },

    .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
    .BufferCount = 2,

    // we don't want any automatic scaling of window content
    // this is supported only on FLIP presentation model
    .Scaling = DXGI_SCALING_NONE,

    // use more efficient FLIP presentation model
    // Windows 10 allows to use DXGI_SWAP_EFFECT_FLIP_DISCARD
    // for Windows 8 compatibility use DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
    // for Windows 7 compatibility use DXGI_SWAP_EFFECT_DISCARD
    .SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD,
  };

  HRESULT hr = IDXGIFactory2_CreateSwapChainForHwnd(
      r_d3d11_state->dxgi_factory2,
      (IUnknown *)r_d3d11_state->base_device, 
      hwnd,
      &swapchain_desc,
      0, 0,
      &equip->swapchain
      );

  IDXGISwapChain1_GetBuffer(equip->swapchain, 0, &IID_ID3D11Texture2D, (void **)(&equip->framebuffer));
  ID3D11Device_CreateRenderTargetView(r_d3d11_state->base_device, (ID3D11Resource*)equip->framebuffer,
      0, &equip->framebuffer_rtv);


  IDXGIFactory_MakeWindowAssociation(r_d3d11_state->dxgi_factory2, hwnd, DXGI_MWA_NO_ALT_ENTER);

  return R_D3D11_handle_from_window_equip(equip);
}

r_function void
R_window_unequip(OS_Handle window, R_Handle window_equip) 
{
  R_D3D11_WindowEquip *equip = R_D3D11_window_equip_from_handle(window_equip);
  if(equip->framebuffer_rtv) { ID3D11RenderTargetView_Release(equip->framebuffer_rtv); }
  if(equip->framebuffer) { ID3D11Texture2D_Release(equip->framebuffer); }
  if(equip->swapchain) { IDXGISwapChain_Release(equip->swapchain); }
  OS_release(equip, sizeof(*equip));
}

// The render handle is a nice way to abstract way whatever different ways to describe the
// swapchain we have in different graphics APIs... so we just get and set a pointer with these utilities.
r_function R_Handle
R_D3D11_handle_from_window_equip(R_D3D11_WindowEquip *equip) 
{
  R_Handle handle = {0};
  handle.u64[0] = (U64)equip;
  return handle;
}

r_function R_D3D11_WindowEquip *
R_D3D11_window_equip_from_handle(R_Handle handle) 
{
  R_D3D11_WindowEquip *result = (R_D3D11_WindowEquip *)handle.u64[0];
  return result;
}

r_function void
R_frame_begin(void) 
{
  // NOTE(anton): No-op
  // TODO(anton): But why is there no-op here? Comment this
}

  r_function void
R_frame_end(void)
{
  // TODO(anton): Implement and understand OverflowBufferNode stuff  
}

  r_function void
R_window_start(R_Handle window_equip_handle, Vec2_S64 resolution)
{
  R_D3D11_WindowEquip *wnd = R_D3D11_window_equip_from_handle(window_equip_handle);
  // TODO(anton): Ryan uses Device1 and Context1 here, I need to understand why and know the difference.
  ID3D11Device *device = r_d3d11_state->base_device;
  ID3D11DeviceContext *d_ctx = r_d3d11_state->base_device_context;

  B32 resolution_changed = (wnd->last_resolution.x != resolution.x ||
      wnd->last_resolution.y != resolution.y);
  wnd->last_resolution = resolution;

  // If the resolution changed we need to remake
  // the swap chain and framebuffer. 
  if(resolution_changed)
  {
    ID3D11RenderTargetView_Release(wnd->framebuffer_rtv);
    ID3D11Texture2D_Release(wnd->framebuffer);
    // NOTE(anton): Since we are getting the buffers again I think we can resize it to zero here rather
    // than to the resolution as is done in mmozeiko example?
    HRESULT hr = IDXGISwapChain1_ResizeBuffers(wnd->swapchain, 0, 
        /* width*/0, /* height */0, DXGI_FORMAT_UNKNOWN, 0);
    if(FAILED(hr)) 
    {
      break_debugger();
    }

    IDXGISwapChain1_GetBuffer(wnd->swapchain, 0, &IID_ID3D11Texture2D, (void**)(&wnd->framebuffer));
    ID3D11Device_CreateRenderTargetView(device, (ID3D11Resource*)wnd->framebuffer,
        0, &wnd->framebuffer_rtv);

  }




  // Clear color
  Vec4_F32 clear_color = {0};
  clear_color.x = 0.2f;
  clear_color.y = 0.4f;
  clear_color.z = 0.6f;

  ID3D11DeviceContext_ClearRenderTargetView(d_ctx, wnd->framebuffer_rtv, clear_color.v); 
}


  r_function void 
R_window_submit(R_Handle window_equip, R_PassList *pass_list)
{
  
  R_D3D11_WindowEquip *wnd = R_D3D11_window_equip_from_handle(window_equip);
  ID3D11DeviceContext *d_ctx = r_d3d11_state->base_device_context;
  
  R_PassNode *pass_node = pass_list->first;
  R_Pass *pass = &pass_node->v;
  R_PassParams_UI* params = pass->params_ui;
  
  //- Draw rectangle batches
  for(R_BatchGroup2DNode *group_node = params->rects.first; group_node != 0; group_node = group_node->next)
  {

    // Unpack node
    R_BatchList *batches = &group_node->batches;
    R_BatchGroup2DParams *batch_params = &group_node->params;
    ID3D11Buffer *instance_buffer = R_D3D11_instance_buffer_from_batch_list(batches);
    U64 instance_count = batches->instance_count;
    U64 bytes_per_instance = batches->byte_count/batches->instance_count;
    R_Handle albedo_texture_handle = batch_params->albedo_tex;
    R_D3D11_Tex2D albedo_texture = R_D3D11_tex2d_from_handle(albedo_texture_handle);
    if(R_handle_is_zero(batch_params->albedo_tex)) 
    {
      break_debugger();
    }
    ID3D11SamplerState *sampler = r_d3d11_state->linear_sampler;
    R_Tex2DSampleKind sample_kind = batch_params->albedo_tex_sample_kind;
    switch(sample_kind)
    {
      default:
      case R_Tex2DSampleKind_Nearest: { sampler = r_d3d11_state->nearest_sampler; } break;
      case R_Tex2DSampleKind_Linear:  { sampler = r_d3d11_state->linear_sampler;  } break;
    }

    /*
    Vec2_F32 clip_min = vec2_F32(
        Clamp(0, Min(batch_params->clip.x0, batch_params->clip.x1), wnd->last_resolution.x), 
        Clamp(0, Min(batch_params->clip.y0, batch_params->clip.y1), wnd->last_resolution.y)
        );
    Vec2_F32 clip_max = vec2_F32(
        Clamp(0, Max(batch_params->clip.x0, batch_params->clip.x1), wnd->last_resolution.x), 
        Clamp(0, Max(batch_params->clip.y0, batch_params->clip.y1), wnd->last_resolution.y)
        );
        */
    //Rng2_F32 clip = rng2_F32(vec2_F32(0,0), vec2_F32(wnd->last_resolution.x, wnd->last_resolution.y));

    // Viewport and rasteriser
    {
      Vec2_S64 resolution = wnd->last_resolution;
      D3D11_VIEWPORT d3d11_viewport = { 0.0f, 0.0f, (F32)resolution.x, (F32)resolution.y, 0.0f, 1.0f };

      if(params->viewport.x0 != 0 || params->viewport.x1 != 0 ||
          params->viewport.y0 != 0 || params->viewport.y1 != 0)
      {
        Vec2_F32 dim = dim2_F32(params->viewport);
        d3d11_viewport.TopLeftX = params->viewport.x0;
        d3d11_viewport.TopLeftY = params->viewport.y0;
        d3d11_viewport.Width    = dim.x; 
        d3d11_viewport.Height   = dim.y; 
      }
      ID3D11DeviceContext_RSSetViewports(d_ctx, 1, &d3d11_viewport);
      ID3D11DeviceContext_RSSetState(d_ctx, r_d3d11_state->rasterizer_state);
    }

    // Scissor rect
    {
      D3D11_RECT rect =
      {
        /* .left   = */ 0,
        /* .top    = */ 0,
        /* .right  = */ (LONG)wnd->last_resolution.x,
        /* .bottom = */ (LONG)wnd->last_resolution.y,
      };
      /* if(clip.x0 != 0 || clip.y0 != 0 || clip.x1 != 0 || clip.y1 != 0) */
      /* { */
      /*   rect.left   = (LONG)clip.x0; */
      /*   rect.right  = (LONG)clip.x1; */
      /*   rect.top    = (LONG)clip.y0; */
      /*   rect.bottom = (LONG)clip.y1; */
      /* } */
      ID3D11DeviceContext_RSSetScissorRects(d_ctx, 1, &rect);
    }

    // Output merger
    {
      ID3D11DeviceContext_OMSetRenderTargets(d_ctx, 1, &wnd->framebuffer_rtv, 0);
      ID3D11DeviceContext_OMSetBlendState(d_ctx, r_d3d11_state->main_blend_state, 0, 0xffffffff);
      ID3D11DeviceContext_OMSetDepthStencilState(d_ctx, r_d3d11_state->depth_stencil_state, 0);
    }

    // Get pipeline objects
    ID3D11Buffer *cmd_global_buffer = r_d3d11_state->cmd_global_buffer_table[R_D3D11_CmdGlobalKind_Rect2D];
    ID3D11VertexShader *vs = r_d3d11_state->vs_table[R_D3D11_ShaderPairKind_Rect2D];
    ID3D11PixelShader *ps = r_d3d11_state->ps_table[R_D3D11_ShaderPairKind_Rect2D];
    ID3D11InputLayout *input_layout = r_d3d11_state->input_layout_table[R_D3D11_ShaderPairKind_Rect2D];

    // Send per-cmd globals
    R_D3D11_CmdGlobals_Rect2D cmd_globals = {0};
    {
      cmd_globals.viewport_size = vec2_F32_from_vec(wnd->last_resolution);
      cmd_globals.albedo_t2d_size = vec2_F32_from_vec(albedo_texture.size);
      cmd_globals.transform[0] = vec3_F32(batch_params->xform2d.elements[0][0],
                                          batch_params->xform2d.elements[1][0],
                                          batch_params->xform2d.elements[2][0]);
      cmd_globals.transform[1] = vec3_F32(batch_params->xform2d.elements[0][1],
                                          batch_params->xform2d.elements[1][1],
                                          batch_params->xform2d.elements[2][1]);
      cmd_globals.transform[2] = vec3_F32(batch_params->xform2d.elements[0][2],
                                          batch_params->xform2d.elements[1][2],
                                          batch_params->xform2d.elements[2][2]);
    }
    
    // TODO(anton): Make this into a function
    {
      D3D11_MAPPED_SUBRESOURCE sub_resource = {0};
      ID3D11DeviceContext_Map(d_ctx, (ID3D11Resource *)cmd_global_buffer, 0, 
          D3D11_MAP_WRITE_DISCARD, 0, &sub_resource);
      String8 data = str8_struct(&cmd_globals);
      U8 *ptr = (U8 *)sub_resource.pData;
      MemoryCopy(ptr, data.str, data.size);
      ID3D11DeviceContext_Unmap(d_ctx, (ID3D11Resource *)cmd_global_buffer, 0);
    }

    // Setup input assembly
    U32 stride = bytes_per_instance;
    U32 offset = 0;
    ID3D11DeviceContext_IASetPrimitiveTopology(d_ctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D11DeviceContext_IASetInputLayout(d_ctx, input_layout);
    ID3D11DeviceContext_IASetVertexBuffers(d_ctx, 0, 1, &instance_buffer, &stride, &offset);

    // Setup shaders
    ID3D11DeviceContext_VSSetShader(d_ctx, vs, 0, 0);
    ID3D11DeviceContext_VSSetConstantBuffers(d_ctx, 0, 1, &cmd_global_buffer);
    ID3D11DeviceContext_PSSetShader(d_ctx, ps, 0, 0);
    ID3D11DeviceContext_PSSetConstantBuffers(d_ctx, 0, 1, &cmd_global_buffer);
    ID3D11DeviceContext_PSSetShaderResources(d_ctx, 0, 1, &albedo_texture.view);
    ID3D11DeviceContext_PSSetSamplers(d_ctx, 0, 1, &sampler);

    // Draw 
    ID3D11DeviceContext_DrawInstanced(d_ctx, /* 4 vertices for a rect */4, instance_count, 0, 0);
  }

}



  r_function void
R_window_finish(R_Handle window_eqp) 
{
  R_D3D11_WindowEquip *wnd = R_D3D11_window_equip_from_handle(window_eqp);
  HRESULT hr = IDXGISwapChain1_Present(wnd->swapchain, 1, 0);
  if(FAILED(hr))
  {
    break_debugger();
  }

  ID3D11DeviceContext_ClearState(r_d3d11_state->base_device_context);
}


r_function R_Handle
R_tex2d_font_atlas(Vec2_S64 size, U8 *data) 
{
  D3D11_USAGE usage = data ? D3D11_USAGE_IMMUTABLE : D3D11_USAGE_DEFAULT;
  UINT access_flags = 0;
  // TODO(anton): Make switch on Tex2DKind here when we generalise
  access_flags = 0;
  
  R_Tex2DFormat fmt = R_Tex2DFormat_R8;
  
  D3D11_TEXTURE2D_DESC tex_desc = {0};
  {
    tex_desc.Width              = size.x;
    tex_desc.Height             = size.y;
    tex_desc.MipLevels          = 1;
    tex_desc.ArraySize          = 1;
    tex_desc.Format             = R_D3D11_DXGI_format_from_tex2d_format(fmt);
    tex_desc.SampleDesc.Count   = 1;
    tex_desc.SampleDesc.Quality = 0;
    tex_desc.Usage              = usage;
    tex_desc.BindFlags          = D3D11_BIND_SHADER_RESOURCE;
    tex_desc.CPUAccessFlags     = access_flags;
  }
  
  D3D11_SUBRESOURCE_DATA initial_data = {0};
  {
    initial_data.pSysMem = data;
    initial_data.SysMemPitch = R_bytes_per_pixel_from_tex2d_format(fmt) * size.x;
    initial_data.SysMemSlicePitch = 0;
  }
  
  R_D3D11_Tex2D texture = {0};
  ID3D11Device_CreateTexture2D(r_d3d11_state->base_device, &tex_desc, &initial_data, &texture.texture);
  ID3D11Device_CreateShaderResourceView(r_d3d11_state->base_device, (ID3D11Resource *)texture.texture, 0, &texture.view);
  
  texture.size = vec2_S32_from_vec(size);
  texture.format = fmt;
  return R_D3D11_handle_from_tex2d(texture);
}


// TODO I kind of dont want this release code but I also dont want the debug output when closing app.
#define __ID3D11_Release(t, n) if(r_d3d11_state->##n) { t##_Release(r_d3d11_state->##n); }
// This is mostly to avoid annoying error message from d3d11 debug layer?
r_function void 
R_shutdown()
{
  R_D3D11_Tex2D atlas_texture = R_D3D11_tex2d_from_handle(F_get_state()->atlas.texture);
  if(atlas_texture.texture) { ID3D11Texture2D_Release(atlas_texture.texture); }
  if(atlas_texture.view) { ID3D11ShaderResourceView_Release(atlas_texture.view); }
  
  for(R_D3D11_ShaderPairKind kind = (R_D3D11_ShaderPairKind)(R_D3D11_ShaderPairKind_Nil+1);
      kind < R_D3D11_ShaderPairKind_COUNT;
      kind = (R_D3D11_ShaderPairKind)(kind + 1)) 
  {
    __ID3D11_Release(ID3D11InputLayout, input_layout_table[kind]);  
    __ID3D11_Release(ID3D11VertexShader, vs_table[kind]);  
    __ID3D11_Release(ID3D11PixelShader, ps_table[kind]);  
  }
  
  for(R_D3D11_CmdGlobalKind kind = (R_D3D11_CmdGlobalKind)(R_D3D11_CmdGlobalKind_Nil+1);
      kind < R_D3D11_CmdGlobalKind_COUNT;
      kind = (R_D3D11_CmdGlobalKind)(kind + 1))
  {
    __ID3D11_Release(ID3D11Buffer, cmd_global_buffer_table[kind]);
  }
  
  __ID3D11_Release(ID3D11Buffer, scratch_buffer_64kb);
  __ID3D11_Release(ID3D11Buffer, scratch_buffer_8mb);
  __ID3D11_Release(ID3D11SamplerState, nearest_sampler);//ID3D11SamplerState_Release(r_d3d11_state->nearest_sampler);
  __ID3D11_Release(ID3D11SamplerState, linear_sampler);//ID3D11SamplerState_Release(r_d3d11_state->linear_sampler);
  __ID3D11_Release(ID3D11DepthStencilState, depth_stencil_state);
  __ID3D11_Release(ID3D11BlendState, main_blend_state);
  __ID3D11_Release(ID3D11RasterizerState, rasterizer_state);//ID3D11RasterizerState_Release(r_d3d11_state->rasterizer_state);
  __ID3D11_Release(IDXGIFactory2, dxgi_factory2);
  __ID3D11_Release(IDXGIAdapter, dxgi_adapter);
  __ID3D11_Release(IDXGIDevice, dxgi_device);
  __ID3D11_Release(ID3D11Device, base_device);
  __ID3D11_Release(ID3D11DeviceContext, base_device_context);
}
