/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2018-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 "vk_test.h"

RD_TEST(VK_CBuffer_Zoo, VulkanGraphicsTest)
{
  static constexpr const char *Description =
      "Tests every kind of constant that can be in a cbuffer to make sure it's decoded correctly.";

  std::string glslpixel = R"EOSHADER(
#version 460 core

layout(location = 0, index = 0) out vec4 Color;

struct vec3_1 { vec3 a; float b; };

struct nested { vec3_1 a; vec4 b[4]; vec3_1 c[4]; };

struct float2_struct { float x; float y; };

struct nested_with_padding
{
  float a;                              // 0, <1, 2, 3>
  vec4 b;                               // {4, 5, 6, 7}
  float c;                              // 8, <9, 10, 11>
  vec3 d[4];                            // [0]: {12, 13, 14}, <15>
                                        // [1]: {16, 17, 18}, <19>
                                        // [2]: {20, 21, 22}, <23>
                                        // [3]: {24, 25, 26}, <27>
};

struct misaligned_struct
{
  vec4 a;
  vec2 b;
};

layout(set = 0, binding = 0, std140) uniform constsbuf
{
  // dummy* entries are just to 'reset' packing to avoid pollution between tests

  vec4 a;                               // basic vec4 = {0, 1, 2, 3}
  vec3 b;                               // should have a padding word at the end = {4, 5, 6}, <7>

  vec2 c; vec2 d;                       // should be packed together = {8, 9}, {10, 11}
  float e; vec3 f;                      // can't be packed together = 12, <13, 14, 15>, {16, 17, 18}, <19>
  vec4 dummy0;
  float j; vec2 k;                      // should have a padding word before the vec2 = 24, <25>, {26, 27}
  vec2 l; float m;                      // should have a padding word at the end = {28, 29}, 30, <31>

  float n[4];                           // should cover 4 vec4s = 32, <33..35>, 36, <37..39>, 40, <41..43>, 44
  vec4 dummy1;

  float o[4];                           // should cover 4 vec4s = 52, <53..55>, 56, <57..59>, 60, <61..63>, 64
  float p;                              // can't be packed in with above array = 68, <69, 70, 71>
  vec4 dummy2;

  layout(column_major) mat4x4 q;        // should cover 4 vec4s.
                                        // row0: {76, 80, 84, 88}
                                        // row1: {77, 81, 85, 89}
                                        // row2: {78, 82, 86, 90}
                                        // row3: {79, 83, 87, 91}
  layout(row_major) mat4x4 r;           // should cover 4 vec4s
                                        // row0: {92, 93, 94, 95}
                                        // row1: {96, 97, 98, 99}
                                        // row2: {100, 101, 102, 103}
                                        // row3: {104, 105, 106, 107}

  layout(column_major) mat4x3 s;        // covers 4 vec4s with padding at end of each column
                                        // row0: {108, 112, 116, 120}
                                        // row1: {109, 113, 117, 121}
                                        // row2: {110, 114, 118, 122}
                                        //       <111, 115, 119, 123>
  vec4 dummy3;
  layout(row_major) mat4x3 t;           // covers 3 vec4s with no padding
                                        // row0: {128, 129, 130, 131}
                                        // row1: {132, 133, 134, 135}
                                        // row2: {136, 137, 138, 139}
  vec4 dummy4;

  layout(column_major) mat3x2 u;        // covers 3 vec4s with padding at end of each column (but not row)
                                        // row0: {144, 148, 152}
                                        // row1: {145, 149, 153}
                                        //       <146, 150, 154>
                                        //       <147, 151, 155>
  vec4 dummy5;
  layout(row_major) mat3x2 v;           // covers 2 vec4s with padding at end of each row (but not column)
                                        // row0: {160, 161, 162}, <163>
                                        // row1: {164, 165, 166}, <167>
  vec4 dummy6;

  layout(column_major) mat2x2 w;        // covers 2 vec4s with padding at end of each column (but not row)
                                        // row0: {172, 176}
                                        // row1: {173, 177}
                                        //       <174, 178>
                                        //       <175, 179>
  vec4 dummy7;
  layout(row_major) mat2x2 x;           // covers 2 vec4s with padding at end of each row (but not column)
                                        // row0: {184, 185}, <186, 187>
                                        // row1: {188, 189}, <190, 191>
  vec4 dummy8;

  layout(row_major) mat2x2 y;           // covers the same as above, and checks z doesn't overlap
                                        // row0: {196, 197}, <198, 199>
                                        // row1: {200, 201}, <202, 203>
  float z;                              // can't overlap = 204, <205, 206, 207>

  // GL Doesn't have single-column matrices
/*
  layout(row_major) mat1x4 aa;          // covers 4 vec4s with maximum padding
                                        // row0: {208}, <209, 210, 211>
                                        // row1: {212}, <213, 214, 215>
                                        // row2: {216}, <217, 218, 219>
                                        // row3: {220}, <221, 222, 223>

  layout(column_major) mat1x4 ab;       // covers 1 vec4 (equivalent to a plain vec4)
                                        // row0: {224}
                                        // row1: {225}
                                        // row2: {226}
                                        // row3: {227}
*/
  vec4 dummy9[5];

  vec4 multiarray[3][2];                // [0][0] = {228, 229, 230, 231}
                                        // [0][1] = {232, 233, 234, 235}
                                        // [1][0] = {236, 237, 238, 239}
                                        // [1][1] = {240, 241, 242, 243}
                                        // [2][0] = {244, 245, 246, 247}
                                        // [2][1] = {248, 249, 250, 251}

  nested structa[2];                    // [0] = {
                                        //   .a = { { 252, 253, 254 }, 255 }
                                        //   .b[0] = { 256, 257, 258, 259 }
                                        //   .b[1] = { 260, 261, 262, 263 }
                                        //   .b[2] = { 264, 265, 266, 267 }
                                        //   .b[3] = { 268, 269, 270, 271 }
                                        //   .c[0] = { { 272, 273, 274 }, 275 }
                                        //   .c[1] = { { 276, 277, 278 }, 279 }
                                        //   .c[2] = { { 280, 281, 282 }, 283 }
                                        //   .c[3] = { { 284, 285, 286 }, 287 }
                                        // }
                                        // [1] = {
                                        //   .a = { { 288, 289, 290 }, 291 }
                                        //   .b[0] = { 292, 293, 294, 295 }
                                        //   .b[1] = { 296, 297, 298, 299 }
                                        //   .b[2] = { 300, 301, 302, 303 }
                                        //   .b[3] = { 304, 305, 306, 307 }
                                        //   .c[0] = { { 308, 309, 310 }, 311 }
                                        //   .c[1] = { { 312, 313, 314 }, 315 }
                                        //   .c[2] = { { 316, 317, 318 }, 319 }
                                        //   .c[3] = { { 320, 321, 322 }, 323 }
                                        // }

  layout(column_major) mat2x3 ac;       // covers 2 vec4s with padding at end of each column (but not row)
                                        // row0: {324, 328}
                                        // row1: {325, 329}
                                        // row2: {326, 330}
                                        //       <327, 331>
  layout(row_major) mat2x3 ad;          // covers 3 vec4s with padding at end of each row (but not column)
                                        // row0: {332, 333}, <334, 335>
                                        // row1: {336, 337}, <338, 339>
                                        // row2: {340, 341}, <342, 343>

  layout(column_major) mat2x3 ae[2];    // covers 2 vec4s with padding at end of each column (but not row)
                                        // [0] = {
                                        //   row0: {344, 348}
                                        //   row1: {345, 349}
                                        //   row2: {346, 350}
                                        //         <347, 351>
                                        // }
                                        // [1] = {
                                        //   row0: {352, 356}
                                        //   row1: {353, 357}
                                        //   row2: {354, 358}
                                        //         <355, 359>
                                        // }
  layout(row_major) mat2x3 af[2];       // covers 3 vec4s with padding at end of each row (but not column)
                                        // [0] = {
                                        //   row0: {360, 361}, <362, 363>
                                        //   row1: {364, 365}, <366, 367>
                                        //   row2: {368, 369}, <370, 371>
                                        // }
                                        // [1] = {
                                        //   row0: {372, 373}, <374, 375>
                                        //   row1: {376, 377}, <378, 379>
                                        //   row2: {380, 381}, <382, 383>
                                        // }

  vec2 dummy10;                         // should have padding at the end = {384, 385}, <386, 387>

  layout(row_major) mat2x2 ag;          // each row is aligned to float4:
                                        // row0: {388, 389}, <390, 391>
                                        // row1: {392, 393}, <394, 395>

  vec2 dummy11;                         // should have padding at the end = {396, 397}, <398, 399>

  layout(column_major) mat2x2 ah;       // each column is aligned to float4:
                                        // row0: {400, 404}
                                        // row1: {401, 405}
                                        //       <402, 406>
                                        //       <403, 407>

  layout(row_major) mat2x2 ai[2];       // [0] = {
                                        //   row0: {408, 409}, <410, 411>
                                        //   row1: {412, 413}, <414, 415>
                                        // }
                                        // [1] = {
                                        //   row0: {416, 417}, <418, 419>
                                        //   row1: {420, 421}, <422, 423>
                                        // }
  layout(column_major) mat2x2 aj[2];    // [0] = {
                                        //   row0: {424, 428}
                                        //   row1: {425, 429}
                                        //         <426, 430>
                                        //         <427, 431>
                                        // }
                                        // [1] = {
                                        //   row0: {432, 436}
                                        //   row1: {433, 437}
                                        //         <434, 438>
                                        //         <435, 439>
                                        // }

  nested_with_padding ak[2];            // 440 - 467, 468 - 495

  vec4 dummy12;                         // forces no trailing overlap with ak

  float al;                             // {500}, <501, 502, 503>

  // struct is always float4 aligned, can't be packed with al
  float2_struct am;                     // {504, 505}, <506, 507>

  // struct doesn't allow trailing things into padding
  float an;                             // {508}

  vec4 dummy13[2];                      // empty structs on D3D

  misaligned_struct ao[2];              // [0] = {
                                        //   .a = { 520, 521, 522, 523 }
                                        //   .b = { 524, 525 } <526, 527>
                                        // }
                                        // [1] = {
                                        //   .a = { 528, 529, 530, 531 }
                                        //   .b = { 532, 533 } <534, 535>
                                        // }

  vec4 test;                            // {536, 537, 538, 539}
};

// this comes from inline uniform block data, where available

layout(set = 0, binding = 1, std140) uniform inlineconsts
{
  vec4 inline_zero;
  vec4 inline_a;
  vec2 inline_b, inline_c;
  vec3_1 inline_d;
};

layout (constant_id = 0) const int A = 10;
layout (constant_id = 1) const float B = 0;
layout (constant_id = 3) const bool C = false;

void main()
{
  Color = test + inline_zero + vec4(0.1f, 0.0f, 0.0f, 0.0f);
}

)EOSHADER";

  std::string hlslpixel = R"EOSHADER(

struct float3_1 { float3 a; float b; };

struct nested { float3_1 a; float4 b[4]; float3_1 c[4]; };

struct float2_struct { float x; float y; };

struct nested_with_padding
{
  float a;                              // 0, <1, 2, 3>
  float4 b;                             // {4, 5, 6, 7}
  float c;                              // 8, <9, 10, 11>
  float3 d[4];                          // [0]: {12, 13, 14}, <15>
                                        // [1]: {16, 17, 18}, <19>
                                        // [2]: {20, 21, 22}, <23>
                                        // [3]: {24, 25, 26}, <27>
};

struct misaligned_struct
{
  float4 a;
  float2 b;
};

layout(set = 0, binding = 0) cbuffer consts
{
  // dummy* entries are just to 'reset' packing to avoid pollution between tests

  float4 a;                               // basic float4 = {0, 1, 2, 3}
  float3 b;                               // should have a padding word at the end = {4, 5, 6}, <7>

  float2 c; float2 d;                     // should be packed together = {8, 9}, {10, 11}
  float e; float3 f;                      // should be packed together = 12, {13, 14, 15}
  float g; float2 h; float i;             // should be packed together = 16, {17, 18}, 19
  float j; float2 k;                      // should have a padding word at the end = 20, {21, 22}, <23>
  float2 l; float m;                      // should have a padding word at the end = {24, 25}, 26, <27>

  float n[4];                             // should cover 4 float4s = 28, <29..31>, 32, <33..35>, 36, <37..39>, 40
  float4 dummy1;

  float o[4];                             // should cover 4 float4s = 48, <..>, 52, <..>, 56, <..>, 60
  float p;                                // with glslang regression to scalar packing, is packed after = 61
  float3 glslang_regression;
  float4 dummy2;
  float4 gldummy;

  // HLSL majorness is flipped to match column-major SPIR-V with row-major HLSL.
  // This means column major declared matrices will show up as row major in any reflection and SPIR-V
  // it also means that dimensions are flipped, so a float3x4 is declared as a float4x3, and a 'row'
  // is really a column, and vice-versa a 'column' is really a row.

  column_major float4x4 q;                // should cover 4 float4s.
                                          // row1: {76, 77, 78, 79}
                                          // row2: {80, 81, 82, 83}
                                          // row3: {84, 85, 86, 87}
                                          // row3: {88, 89, 90, 91}
  row_major float4x4 r;                   // should cover 4 float4s
                                          // row0: {92, 96, 100, 104}
                                          // row1: {93, 97, 101, 105}
                                          // row2: {94, 98, 102, 106}
                                          // row3: {95, 99, 103, 107}

  column_major float3x4 s;                // covers 4 float4s with padding at end of each 'row'
                                          // row0: {108, 109, 110}, <111>
                                          // row1: {112, 113, 114}, <115>
                                          // row2: {116, 117, 118}, <119>
                                          // row3: {120, 121, 122}, <123>
  float4 dummy3;
  row_major float3x4 t;                   // covers 3 float4s with no padding
                                          // row0: {128, 132, 136}
                                          // row1: {129, 133, 137}
                                          // row2: {130, 134, 138}
                                          // row3: {131, 135, 139}
  float4 dummy4;

  column_major float2x3 u;                // covers 3 float4s with padding at end of each 'row' (but not 'column')
                                          // row0: {144, 145}, <146, 147>
                                          // row1: {148, 149}, <150, 151>
                                          // row2: {152, 153}, <154, 155>
  float4 dummy5;
  row_major float2x3 v;                   // covers 2 float4s with padding at end of each 'column' (but not 'row')
                                          // row0: {160, 164}
                                          // row1: {161, 165}
                                          // row2: {162, 166}
                                          //       <163, 167>
  float4 dummy6;

  column_major float2x2 w;                // covers 2 float4s with padding at end of each 'row' (but not 'column')
                                          // row0: {172, 173}, <174, 175>
                                          // row1: {176, 177}, <178, 179>
  float4 dummy7;
  row_major float2x2 x;                   // covers 2 float4s with padding at end of each 'column' (but not 'row')
                                          // row0: {184, 188}
                                          // row1: {185, 189}
                                          //       <186, 190>
                                          //       <187, 191>
  float4 dummy8;

  row_major float2x2 y;                   // covers the same as above, proving z doesn't overlap
                                          // row0: {196, 200}
                                          // row1: {197, 201}
                                          //       <198, 202>
                                          //       <199, 203>
  float z;                                // with glslang regression to scalar packing, is packed after = 202
  float3 glslang_regression2;

  // SPIR-V can't represent single-dimension matrices properly at the moment
/*
  row_major float4x1 aa;                  // covers 4 float4s with maximum padding
                                          // row0: {208, 212, 216, 220}
                                          //       <209, 213, 217, 221>
                                          //       <210, 214, 218, 222>
                                          //       <211, 215, 219, 223>

  column_major float4x1 ab;               // covers 1 float4 (equivalent to a plain float4 after row/column swap)
                                          // row0: {224, 225, 226, 227}
*/
  float4 dummy9[5];

  float4 multiarray[3][2];                // [0][0] = {228, 229, 230, 231}
                                          // [0][1] = {232, 233, 234, 235}
                                          // [1][0] = {236, 237, 238, 239}
                                          // [1][1] = {240, 241, 242, 243}
                                          // [2][0] = {244, 245, 246, 247}
                                          // [2][1] = {248, 249, 250, 251}

  nested structa[2];                      // [0] = {
                                          //   .a = { { 252, 253, 254 }, 255 }
                                          //   .b[0] = { 256, 257, 258, 259 }
                                          //   .b[1] = { 260, 261, 262, 263 }
                                          //   .b[2] = { 264, 265, 266, 267 }
                                          //   .b[3] = { 268, 269, 270, 271 }
                                          //   .c[0] = { { 272, 273, 274 }, 275 }
                                          //   .c[1] = { { 276, 277, 278 }, 279 }
                                          //   .c[2] = { { 280, 281, 282 }, 283 }
                                          //   .c[3] = { { 284, 285, 286 }, 287 }
                                          // }
                                          // [1] = {
                                          //   .a = { { 288, 289, 290 }, 291 }
                                          //   .b[0] = { 292, 293, 294, 295 }
                                          //   .b[1] = { 296, 297, 298, 299 }
                                          //   .b[2] = { 300, 301, 302, 303 }
                                          //   .b[3] = { 304, 305, 306, 307 }
                                          //   .c[0] = { { 308, 309, 310 }, 311 }
                                          //   .c[1] = { { 312, 313, 314 }, 315 }
                                          //   .c[2] = { { 316, 317, 318 }, 319 }
                                          //   .c[3] = { { 320, 321, 322 }, 323 }
                                          // }

  column_major float3x2 ac;               // covers 2 float4s with padding at end of each column (but not row)
                                          // row0: {324, 328}
                                          // row1: {325, 329}
                                          // row2: {326, 330}
                                          //       <327, 331>
  row_major float3x2 ad;                  // covers 3 float4s with padding at end of each row (but not column)
                                          // row0: {332, 333}, <334, 335>
                                          // row1: {336, 337}, <338, 339>
                                          // row2: {340, 341}, <342, 343>

  column_major float3x2 ae[2];            // covers 2 float4s with padding at end of each column (but not row)
                                          // [0] = {
                                          //   row0: {344, 348}
                                          //   row1: {345, 349}
                                          //   row2: {346, 350}
                                          //         <347, 351>
                                          // }
                                          // [1] = {
                                          //   row0: {352, 356}
                                          //   row1: {353, 357}
                                          //   row2: {354, 358}
                                          //         <355, 359>
                                          // }
  row_major float3x2 af[2];               // covers 3 float4s with padding at end of each row (but not column)
                                          // [0] = {
                                          //   row0: {360, 361}, <362, 363>
                                          //   row1: {364, 365}, <366, 367>
                                          //   row2: {368, 369}, <370, 371>
                                          // }
                                          // [1] = {
                                          //   row0: {372, 373}, <374, 375>
                                          //   row1: {376, 377}, <378, 379>
                                          //   row2: {380, 381},
                                          // }

  float2 dummy10;                          // consumes leftovers from above array = {382, 383}

  float2 dummy11;                         // should have padding at the end = {384, 385}, <386, 387>

  row_major float2x2 ag;                  // each row is aligned to float4:
                                          // row0: {388, 389}, <390, 391>
                                          // row1: {392, 393},

  float2 dummy12;                         // consumes leftovers from above matrix = {394, 395}
  float2 dummy13;                         // should have padding at the end = {396, 397}, <398, 399>

  column_major float2x2 ah;               // each column is aligned to float4:
                                          // row0: {400, 404}
                                          // row1: {401, 405}
                                          //       <402, 406>
                                          //       <403, 407>

  row_major float2x2 ai[2];               // [0] = {
                                          //   row0: {408, 409}, <410, 411>
                                          //   row1: {412, 413}, <414, 415>
                                          // }
                                          // [1] = {
                                          //   row0: {416, 417}, <418, 419>
                                          //   row1: {420, 421}, <422, 423>
                                          // }
  column_major float2x2 aj[2];            // [0] = {
                                          //   row0: {424, 428}
                                          //   row1: {425, 429}
                                          //         <426, 430>
                                          //         <427, 431>
                                          // }
                                          // [1] = {
                                          //   row0: {432, 436}
                                          //   row1: {433, 437}
                                          //         <434, 438>
                                          //         <435, 439>
                                          // }
                                          
  nested_with_padding ak[2];              // 440 - 467, 468 - 494
  
  float4 dummy14;                         // forces no trailing overlap with ak

  float al;                               // {500}, <501, 502, 503>

  // struct is always float4 aligned, can't be packed with al
  float2_struct am;                       // {504, 505}, <506, 507>

  // struct doesn't allow trailing things into padding
  float an;                               // {508}
  
  float4 dummy15[2];                      // empty structs on D3D

  misaligned_struct ao[2];                // [0] = {
                                          //   .a = { 520, 521, 522, 523 }
                                          //   .b = { 524, 525 } <526, 527>
                                          // }
                                          // [1] = {
                                          //   .a = { 528, 529, 530, 531 }
                                          //   .b = { 532, 533 } <534, 535>
                                          // }

  float4 test;                            // {536, 537, 538, 539}
};

// this comes from inline uniform block data, where available
layout(set = 0, binding = 1) cbuffer inlineconsts
{
  float4 inline_zero;
  float4 inline_a;
  float2 inline_b, inline_c;
  float3_1 inline_d;
};

float4 main() : SV_Target0
{
	return test + inline_zero + float4(0.1f, 0.0f, 0.0f, 0.0f);
}

)EOSHADER";

  struct float3_1
  {
    float a[3];
    float b;
  };

  struct InlineData
  {
    float inline_zero[4];
    float inline_a[4];
    float inline_b[2], inline_c[2];
    float3_1 inline_d;
  };

  void Prepare(int argc, char **argv)
  {
    devExts.push_back(VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME);
    optDevExts.push_back(VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME);

    // new glslang has a regression that requires scalar block layout for hlsl and does not support
    // generating non-scalar layout hlsl packing
    devExts.push_back(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);

    VulkanGraphicsTest::Prepare(argc, argv);

    static VkPhysicalDeviceInlineUniformBlockFeaturesEXT inlineFeats = {
        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT,
    };

    if(hasExt(VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME))
    {
      inlineFeats.inlineUniformBlock = VK_TRUE;
      inlineFeats.pNext = (void *)devInfoNext;
      devInfoNext = &inlineFeats;
    }

    static VkPhysicalDeviceScalarBlockLayoutFeaturesEXT scalarFeatures = {
        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT,
    };

    getPhysFeatures2(&scalarFeatures);

    if(!scalarFeatures.scalarBlockLayout)
      Avail = "Scalar block layout feature 'scalarBlockLayout' not available";

    scalarFeatures.pNext = (void *)devInfoNext;

    devInfoNext = &scalarFeatures;
  }

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

    VkDescriptorType descType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    uint32_t descCount = 1;

    if(hasExt(VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME))
    {
      descType = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT;
      descCount = 16 * sizeof(float);
    }

    VkDescriptorSetLayout setlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({
        {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
        {1, descType, descCount, VK_SHADER_STAGE_FRAGMENT_BIT},
    }));

    VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo({setlayout}));

    AllocatedImage img(
        this,
        vkh::ImageCreateInfo(mainWindow->scissor.extent.width, mainWindow->scissor.extent.height, 0,
                             VK_FORMAT_R32G32B32A32_SFLOAT,
                             VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT),
        VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY}));

    VkImageView imgview = createImageView(
        vkh::ImageViewCreateInfo(img.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R32G32B32A32_SFLOAT));

    vkh::RenderPassCreator renderPassCreateInfo;

    renderPassCreateInfo.attachments.push_back(
        vkh::AttachmentDescription(VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_UNDEFINED,
                                   VK_IMAGE_LAYOUT_GENERAL, VK_ATTACHMENT_LOAD_OP_CLEAR));

    renderPassCreateInfo.addSubpass({VkAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL})});

    VkRenderPass renderPass = createRenderPass(renderPassCreateInfo);

    VkFramebuffer framebuffer = createFramebuffer(
        vkh::FramebufferCreateInfo(renderPass, {imgview}, mainWindow->scissor.extent));

    vkh::GraphicsPipelineCreateInfo pipeCreateInfo;

    pipeCreateInfo.layout = layout;
    pipeCreateInfo.renderPass = renderPass;

    pipeCreateInfo.vertexInputState.vertexBindingDescriptions = {vkh::vertexBind(0, DefaultA2V)};
    pipeCreateInfo.vertexInputState.vertexAttributeDescriptions = {
        vkh::vertexAttr(0, 0, DefaultA2V, pos),
        vkh::vertexAttr(1, 0, DefaultA2V, col),
        vkh::vertexAttr(2, 0, DefaultA2V, uv),
    };

    pipeCreateInfo.stages = {
        CompileShaderModule(VKDefaultVertex, ShaderLang::glsl, ShaderStage::vert, "main"),
        CompileShaderModule(glslpixel, ShaderLang::glsl, ShaderStage::frag, "main"),
    };

    float data[2] = {20.0f, 0.0f};

    // data[1] is a bool
    VkBool32 btrue = true;
    memcpy(&data[1], &btrue, sizeof(btrue));

    VkSpecializationMapEntry specmap[2] = {
        {1, 0, sizeof(float)},
        {3, 4, sizeof(VkBool32)},
    };

    VkSpecializationInfo spec = {};
    spec.mapEntryCount = 2;
    spec.pMapEntries = specmap;
    spec.dataSize = sizeof(data);
    spec.pData = data;

    pipeCreateInfo.stages[1].pSpecializationInfo = &spec;

    VkPipeline glslpipe = createGraphicsPipeline(pipeCreateInfo);

    pipeCreateInfo.stages[1] =
        CompileShaderModule(hlslpixel, ShaderLang::hlsl, ShaderStage::frag, "main");

    VkPipeline hlslpipe = createGraphicsPipeline(pipeCreateInfo);

    AllocatedBuffer vb(
        this,
        vkh::BufferCreateInfo(sizeof(DefaultTri),
                              VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT),
        VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));

    vb.upload(DefaultTri);

    const size_t bindOffset = 16;

    Vec4f cbufferdata[512 + bindOffset];

    for(int i = 0; i < bindOffset; i++)
      cbufferdata[i] = Vec4f(-99.9f, -88.8f, -77.7f, -66.6f);

    for(int i = 0; i < 512; i++)
      cbufferdata[i + bindOffset] =
          Vec4f(float(i * 4 + 0), float(i * 4 + 1), float(i * 4 + 2), float(i * 4 + 3));

    AllocatedBuffer cb(
        this,
        vkh::BufferCreateInfo(sizeof(cbufferdata), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
                                                       VK_BUFFER_USAGE_TRANSFER_DST_BIT),
        VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));

    cb.upload(cbufferdata);

    InlineData inlinedata = {};

    inlinedata.inline_a[0] = 10.0f;
    inlinedata.inline_a[1] = 20.0f;
    inlinedata.inline_a[2] = 30.0f;
    inlinedata.inline_a[3] = 40.0f;

    inlinedata.inline_b[0] = 50.0f;
    inlinedata.inline_b[1] = 60.0f;

    inlinedata.inline_c[0] = 70.0f;
    inlinedata.inline_c[1] = 80.0f;

    inlinedata.inline_d.a[0] = 90.0f;
    inlinedata.inline_d.a[1] = 100.0f;
    inlinedata.inline_d.a[2] = 110.0f;
    inlinedata.inline_d.b = 120.0f;

    AllocatedBuffer inlinecb(
        this,
        vkh::BufferCreateInfo(sizeof(cbufferdata), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
                                                       VK_BUFFER_USAGE_TRANSFER_DST_BIT),
        VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));

    {
      byte *ptr = inlinecb.map();
      memcpy(ptr + bindOffset * sizeof(Vec4f), &inlinedata, sizeof(inlinedata));
      inlinecb.unmap();
    }

    VkDescriptorSet descset = allocateDescriptorSet(setlayout);

    vkh::updateDescriptorSets(
        device, {
                    vkh::WriteDescriptorSet(
                        descset, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
                        {vkh::DescriptorBufferInfo(cb.buffer, bindOffset * sizeof(Vec4f))}),
                });

    if(hasExt(VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME))
    {
      vkh::updateDescriptorSets(
          device,
          {
              vkh::WriteDescriptorSet(
                  descset, 1,
                  vkh::WriteDescriptorSetInlineUniformBlockEXT(&inlinedata, sizeof(inlinedata)),
                  vkh::DescriptorBufferInfo(cb.buffer, bindOffset * sizeof(Vec4f))),
          });
    }
    else
    {
      vkh::updateDescriptorSets(
          device, {
                      vkh::WriteDescriptorSet(
                          descset, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
                          {vkh::DescriptorBufferInfo(inlinecb.buffer, bindOffset * sizeof(Vec4f))}),
                  });
    }

    while(Running())
    {
      VkCommandBuffer cmd = GetCommandBuffer();

      vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());

      VkImage swapimg =
          StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL);

      vkCmdBeginRenderPass(cmd,
                           vkh::RenderPassBeginInfo(renderPass, framebuffer, mainWindow->scissor,
                                                    {vkh::ClearValue(0.2f, 0.2f, 0.2f, 1.0f)}),
                           VK_SUBPASS_CONTENTS_INLINE);

      vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, {descset}, {});
      vkCmdSetViewport(cmd, 0, 1, &mainWindow->viewport);
      vkCmdSetScissor(cmd, 0, 1, &mainWindow->scissor);
      vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0});

      vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, glslpipe);
      vkCmdDraw(cmd, 3, 1, 0, 0);

      vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, hlslpipe);
      vkCmdDraw(cmd, 3, 1, 0, 0);

      vkCmdEndRenderPass(cmd);

      vkh::cmdPipelineBarrier(
          cmd, {
                   vkh::ImageMemoryBarrier(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
                                           VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL,
                                           VK_IMAGE_LAYOUT_GENERAL, img.image),
               });

      blitToSwap(cmd, img.image, VK_IMAGE_LAYOUT_GENERAL, swapimg, VK_IMAGE_LAYOUT_GENERAL);

      FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL);

      vkEndCommandBuffer(cmd);

      Submit(0, 1, {cmd});

      Present();
    }

    return 0;
  }
};

REGISTER_TEST();
