~sircmpwn/xrgears

b985da1fdfc018d854c9e2b3da297285e6b21014 — Lubosz Sarnecki 4 years ago 3e10884
make demo work.
M CMakeLists.txt => CMakeLists.txt +24 -3
@@ 1,5 1,26 @@

cmake_minimum_required(VERSION 3.8)
project(xrgears)

add_executable(xrgears
  ${CMAKE_SOURCE_DIR}/src/gears.cpp
  ${CMAKE_SOURCE_DIR}/src/vulkangear.cpp)
# use XCB

# find_package(XCB REQUIRED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DVK_USE_PLATFORM_XCB_KHR")

set(XRGEARS_SRC
    ${CMAKE_SOURCE_DIR}/src/gears.cpp
    ${CMAKE_SOURCE_DIR}/src/vulkangear.cpp

    ${CMAKE_SOURCE_DIR}/src/vulkantools.cpp
    ${CMAKE_SOURCE_DIR}/src/vulkanexamplebase.cpp
    ${CMAKE_SOURCE_DIR}/src/vulkandebug.cpp
)

FILE(GLOB_RECURSE LibFiles "${CMAKE_SOURCE_DIR}/src/*.hpp")
add_custom_target(headers SOURCES ${LibFiles})

FILE(GLOB_RECURSE XRGEARS_SRC "${CMAKE_SOURCE_DIR}/src/*.cpp")
#include_directories("src")
add_executable(xrgears ${XRGEARS_SRC})

target_link_libraries(xrgears vulkan xcb assimp)

A data/shaders/base/textoverlay.frag => data/shaders/base/textoverlay.frag +13 -0
@@ 0,0 1,13 @@
#version 450 core

layout (location = 0) in vec2 inUV;

layout (binding = 0) uniform sampler2D samplerFont;

layout (location = 0) out vec4 outFragColor;

void main(void)
{
	float color = texture(samplerFont, inUV).r;
	outFragColor = vec4(vec3(color), 1.0);
}

A data/shaders/base/textoverlay.frag.spv => data/shaders/base/textoverlay.frag.spv +0 -0
A data/shaders/base/textoverlay.vert => data/shaders/base/textoverlay.vert +17 -0
@@ 0,0 1,17 @@
#version 450 core

layout (location = 0) in vec2 inPos;
layout (location = 1) in vec2 inUV;

layout (location = 0) out vec2 outUV;

out gl_PerVertex 
{
    vec4 gl_Position;   
};

void main(void)
{
	gl_Position = vec4(inPos, 0.0, 1.0);
	outUV = inUV;
}

A data/shaders/base/textoverlay.vert.spv => data/shaders/base/textoverlay.vert.spv +0 -0
A data/shaders/gears.frag => data/shaders/gears.frag +24 -0
@@ 0,0 1,24 @@
#version 450

#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable

layout (location = 0) in vec3 inNormal;
layout (location = 1) in vec3 inColor;
layout (location = 2) in vec3 inEyePos;
layout (location = 3) in vec3 inLightVec;

layout (location = 0) out vec4 outFragColor;

void main() 
{
  vec3 Eye = normalize(-inEyePos);
  vec3 Reflected = normalize(reflect(-inLightVec, inNormal)); 
 
  vec4 IAmbient = vec4(0.2, 0.2, 0.2, 1.0);
  vec4 IDiffuse = vec4(0.5, 0.5, 0.5, 0.5) * max(dot(inNormal, inLightVec), 0.0);
  float specular = 0.25;
  vec4 ISpecular = vec4(0.5, 0.5, 0.5, 1.0) * pow(max(dot(Reflected, Eye), 0.0), 0.8) * specular; 
 
  outFragColor = vec4((IAmbient + IDiffuse) * vec4(inColor, 1.0) + ISpecular);
}
\ No newline at end of file

A data/shaders/gears.frag.spv => data/shaders/gears.frag.spv +0 -0
A data/shaders/gears.vert => data/shaders/gears.vert +34 -0
@@ 0,0 1,34 @@
#version 450

#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable

layout (location = 0) in vec4 inPos;
layout (location = 1) in vec3 inNormal;
layout (location = 2) in vec3 inColor;

layout (binding = 0) uniform UBO 
{
	mat4 projection;
	mat4 model;
	mat4 normal;
	mat4 view;
	vec3 lightpos;
} ubo;

layout (location = 0) out vec3 outNormal;
layout (location = 1) out vec3 outColor;
layout (location = 2) out vec3 outEyePos;
layout (location = 3) out vec3 outLightVec;

void main() 
{
	outNormal = normalize(mat3(ubo.normal) * inNormal);
	outColor = inColor;
	mat4 modelView = ubo.view * ubo.model;
	vec4 pos = modelView * inPos;	
	outEyePos = vec3(modelView * pos);
	vec4 lightPos = vec4(ubo.lightpos, 1.0) * modelView;
	outLightVec = normalize(lightPos.xyz - outEyePos);
	gl_Position = ubo.projection * pos;
}
\ No newline at end of file

A data/shaders/gears.vert.spv => data/shaders/gears.vert.spv +0 -0
A data/stb_font_consolas_24_latin1.inl => data/stb_font_consolas_24_latin1.inl +734 -0
@@ 0,0 1,734 @@
// Font generated by stb_font_inl_generator.c (4/1 bpp)
//
// Following instructions show how to use the only included font, whatever it is, in
// a generic way so you can replace it with any other font by changing the include.
// To use multiple fonts, replace STB_SOMEFONT_* below with STB_FONT_consolas_24_latin1_*,
// and separately install each font. Note that the CREATE function call has a
// totally different name; it's just 'stb_font_consolas_24_latin1'.
//
/* // Example usage:

static stb_fontchar fontdata[STB_SOMEFONT_NUM_CHARS];

static void init(void)
{
    // optionally replace both STB_SOMEFONT_BITMAP_HEIGHT with STB_SOMEFONT_BITMAP_HEIGHT_POW2
    static unsigned char fontpixels[STB_SOMEFONT_BITMAP_HEIGHT][STB_SOMEFONT_BITMAP_WIDTH];
    STB_SOMEFONT_CREATE(fontdata, fontpixels, STB_SOMEFONT_BITMAP_HEIGHT);
    ... create texture ...
    // for best results rendering 1:1 pixels texels, use nearest-neighbor sampling
    // if allowed to scale up, use bilerp
}

// This function positions characters on integer coordinates, and assumes 1:1 texels to pixels
// Appropriate if nearest-neighbor sampling is used
static void draw_string_integer(int x, int y, char *str) // draw with top-left point x,y
{
    ... use texture ...
    ... turn on alpha blending and gamma-correct alpha blending ...
    glBegin(GL_QUADS);
    while (*str) {
        int char_codepoint = *str++;
        stb_fontchar *cd = &fontdata[char_codepoint - STB_SOMEFONT_FIRST_CHAR];
        glTexCoord2f(cd->s0, cd->t0); glVertex2i(x + cd->x0, y + cd->y0);
        glTexCoord2f(cd->s1, cd->t0); glVertex2i(x + cd->x1, y + cd->y0);
        glTexCoord2f(cd->s1, cd->t1); glVertex2i(x + cd->x1, y + cd->y1);
        glTexCoord2f(cd->s0, cd->t1); glVertex2i(x + cd->x0, y + cd->y1);
        // if bilerping, in D3D9 you'll need a half-pixel offset here for 1:1 to behave correct
        x += cd->advance_int;
    }
    glEnd();
}

// This function positions characters on float coordinates, and doesn't require 1:1 texels to pixels
// Appropriate if bilinear filtering is used
static void draw_string_float(float x, float y, char *str) // draw with top-left point x,y
{
    ... use texture ...
    ... turn on alpha blending and gamma-correct alpha blending ...
    glBegin(GL_QUADS);
    while (*str) {
        int char_codepoint = *str++;
        stb_fontchar *cd = &fontdata[char_codepoint - STB_SOMEFONT_FIRST_CHAR];
        glTexCoord2f(cd->s0f, cd->t0f); glVertex2f(x + cd->x0f, y + cd->y0f);
        glTexCoord2f(cd->s1f, cd->t0f); glVertex2f(x + cd->x1f, y + cd->y0f);
        glTexCoord2f(cd->s1f, cd->t1f); glVertex2f(x + cd->x1f, y + cd->y1f);
        glTexCoord2f(cd->s0f, cd->t1f); glVertex2f(x + cd->x0f, y + cd->y1f);
        // if bilerping, in D3D9 you'll need a half-pixel offset here for 1:1 to behave correct
        x += cd->advance;
    }
    glEnd();
}
*/

#pragma once

#ifndef STB_FONTCHAR__TYPEDEF
#define STB_FONTCHAR__TYPEDEF
typedef struct
{
    // coordinates if using integer positioning
    float s0,t0,s1,t1;
    signed short x0,y0,x1,y1;
    int   advance_int;
    // coordinates if using floating positioning
    float s0f,t0f,s1f,t1f;
    float x0f,y0f,x1f,y1f;
    float advance;
} stb_fontchar;
#endif

#define STB_FONT_consolas_24_latin1_BITMAP_WIDTH         256
#define STB_FONT_consolas_24_latin1_BITMAP_HEIGHT        170
#define STB_FONT_consolas_24_latin1_BITMAP_HEIGHT_POW2   256

#define STB_FONT_consolas_24_latin1_FIRST_CHAR            32
#define STB_FONT_consolas_24_latin1_NUM_CHARS            224

#define STB_FONT_consolas_24_latin1_LINE_SPACING          16

static unsigned int stb__consolas_24_latin1_pixels[]={
    0x08262131,0xff904400,0x3ffe1fff,0x3b2206ff,0x2007913f,0x0000defa,
    0x64c00f32,0x0de5c402,0x00a614c0,0x4002ae62,0x98014c19,0x01aa881c,
    0x00d54400,0xb880154c,0x8330020b,0x1e980029,0xaa7d5dd4,0x2001d94f,
    0x3332a6e8,0x999ff0ff,0x37ffa207,0x2600df12,0x8000fffd,0x3fa005fd,
    0xfdff700f,0x8ffc409f,0x3ea02ff8,0x200dffff,0x0bfe0ff8,0x7d407ee0,
    0xfd10001f,0x9fff5007,0xcffff880,0x1ff104eb,0x320017fc,0x7d77e40f,
    0x17ee9f54,0xfd027ec0,0x7dc01fe1,0x0037c40d,0xb0017f22,0xffe8007f,
    0x7dc3fd80,0x741ff104,0x59ff701f,0x7401dfd7,0x2003f60e,0x3fa200fc,
    0x0ff44001,0x7dc6fdc0,0x32236604,0x3ba00eff,0x31003f60,0xdf90dd57,
    0xd93ea9f5,0x037e403f,0x803fc3fa,0x06f882fd,0x2006f980,0xa8800098,
    0xf903fb81,0x88040401,0x1ff441ff,0x0fb00000,0x00000000,0x00020000,
    0xffffa800,0xfadfb86f,0x7fc49f54,0x803ff301,0x200ff0fe,0x06f880fe,
    0x0000ff00,0x05f88000,0x40000fe6,0x0ff504fc,0x81540153,0x100affb9,
    0x22001573,0x31000ab9,0x26200157,0x731000ab,0xcffb8015,0x7dc4ffda,
    0x89f54fad,0x3fe80ff9,0x0ff0fe80,0x3e203fa0,0x807ddb36,0x01dd107f,
    0xdddb076c,0x1fb8bddd,0x1dd12fc0,0x3ff076c0,0x3f60ffc0,0xe98ff102,
    0xa84fffff,0x00dfffff,0x37ffffea,0x3fffea00,0x3fea00df,0x2a00dfff,
    0x80dfffff,0x3bb61ff8,0x3eb3ea3f,0x7f909f54,0xfd005fa8,0x7f401fe1,
    0xffaef880,0x1fe05ffe,0xdf504fd8,0xfffffff8,0x9db33746,0x09fb1b6b,
    0x80ff1bea,0x817ec2fd,0x33fe67f8,0x7dc3acfa,0x0efebacf,0xebacffb8,
    0xcffb80ef,0xb80efeba,0xefebacff,0xbacffb80,0x27e40efe,0x20df59f1,
    0x509f54fa,0x003fd8bf,0x401fe1fd,0x9fff107e,0x7407fe61,0x20ff500f,
    0x6f9803fd,0x777ccfe2,0x7fa8db6f,0x37cc7fb0,0x5fb0ff60,0x3fe9fe20,
    0x3ff105f3,0x7c43fe88,0x21ff441f,0x7f441ff8,0x220ffc43,0x0ffc43fe,
    0x3ff0ffa2,0x267f97d4,0x9f54fadf,0x1ff8ff10,0x1fe1fd00,0x7c417e20,
    0x704fb84f,0x217f405f,0xf9800ff8,0x47f47ea6,0xfd0f95f8,0x260ff885,
    0x21fe406f,0x2ff102fd,0x207ea6f9,0x8ff504fc,0x8ff504fc,0x8ff504fc,
    0x8ff504fc,0x8ff504fc,0x3fd3e47f,0x553eafea,0x261ff04f,0x1fd000ff,
    0xefcc81fe,0x260ff101,0x9bfb105f,0x7d45fb81,0x64df3005,0x9f34f98f,
    0x517ee1f2,0x407f98bf,0x817ec2fd,0x5cbf57f8,0x201ff80f,0x807fe1ff,
    0x807fe1ff,0x807fe1ff,0x807fe1ff,0xf8df31ff,0x47e4bf65,0x203514fa,
    0x00df52fd,0x107f87f4,0x7c403fff,0x440df306,0x7fc41ffe,0x4c00bf60,
    0x97ddf66f,0xf10db2fa,0x2217ec1f,0x20ff407f,0x2ff102fd,0x7c1f64fb,
    0xff17ec07,0x1fe2fd80,0x03fc5fb0,0x407f8bf6,0x4cdf32fd,0x9d4ff22f,
    0x9f7004fa,0xfe8013ee,0x6cc40ff0,0x20df103f,0x3bf506f9,0x7c4ff601,
    0xd9be6007,0x47f23f96,0x227fb06d,0x203ff07f,0x17ec0ff8,0x4df57f88,
    0x406f986e,0x80df33fd,0x80df33fd,0x80df33fd,0x80df33fd,0x64ff13fd,
    0x2a0bf60f,0x29f9004f,0x3fa005fa,0x1be00ff0,0x5fa837c4,0xfa801f90,
    0x4c009f76,0x87edba6f,0x2a09f0fd,0x3209f76f,0xb0bf704f,0x4dfe205f,
    0xf30bf0ff,0xf33fc80d,0xf33fc80d,0xf33fc80d,0xf33fc80d,0x3e3fc80d,
    0x03fd1ba7,0x3f6009f5,0x7400ff13,0x3a00ff0f,0x906f880f,0x401fa07f,
    0x001fe9ff,0xf96e9be6,0x017cdfe3,0x203fd3ff,0x7fd43ff9,0xf102fd81,
    0x27d7fecf,0xfb01fe63,0xfb01fe65,0xfb01fe65,0xfb01fe65,0xfb01fe65,
    0x1fc4ff45,0x9f501ff1,0xfe8bfe00,0x3e1fd001,0x101fd007,0x40df50df,
    0xfbf9007e,0x26f9800d,0xfdb9f56d,0x7e401fb7,0x7fdc06fd,0xb02ffede,
    0x89fe205f,0x4feefffc,0x1fe80ff1,0x1fe80ff1,0x1fe80ff1,0x1fe80ff1,
    0x1fe80ff1,0xb87ef7f2,0x2a9f505f,0x647f984f,0x21fd002f,0x01fd007f,
    0xfb99dff1,0x803f403f,0x4003fff8,0x6c5f26f9,0x01ffe8ef,0x401fffc4,
    0x01dfffda,0x2fd40ff2,0x0e77ff4c,0x3fe203ff,0x7c407fe0,0x4407fe0f,
    0x407fe0ff,0x07fe0ff8,0xff107fc4,0x807fd4fd,0x509f54fa,0x004fa8bf,
    0x401fe1fd,0xfff880fe,0x7400deff,0x01ff6007,0x0fb9be60,0x7ec00302,
    0x027dc007,0x4fc827dc,0x3f203f70,0xfc8bf704,0xfc8bf704,0xfc8bf704,
    0xfc8bf704,0xf30bf704,0x07ffd9df,0x84faa7d4,0x07fc41fe,0x07f87f40,
    0xdf101fd0,0x07f80022,0x2000ff60,0x00fe65fa,0x001fec00,0x98103fe6,
    0x0ffcc1ff,0xff100fc8,0x440ffa87,0x07fd43ff,0xff50ffe2,0x543ff881,
    0x1ffc40ff,0x7dc03fea,0x5401dfff,0xfc89f54f,0xd00df705,0x6c01fe1f,
    0x006f883f,0xf5006f98,0x9fb0001f,0xa80006e8,0xfd8000ff,0xfc86fcdf,
    0x04ffecdf,0xdff701f6,0x2e07ffd9,0x3ffeceff,0xfd9dff70,0x3bfee07f,
    0xf703ffec,0x07ffd9df,0x5000cfec,0xfa93ea9f,0x013f600f,0x401fe1fd,
    0x37c41efb,0x013f6600,0x33007fea,0x2e07fdc4,0xf500505f,0x7e40003f,
    0xfd703fff,0x6e805dff,0xdfffea80,0x7fff5401,0x7ff5401d,0x7f5401df,
    0x75401dff,0x7c01dfff,0x54fa8005,0x00bfe29f,0x775c3fd1,0xddff0ffe,
    0x7fff409d,0x2600df12,0xf300effe,0xf7007fff,0x207fffdf,0x7fdcdffb,
    0x07ffff30,0x80022000,0x0017e001,0x00060003,0x0018000c,0x07220030,
    0x7d53ea00,0x26007fb4,0x3bbbae6f,0x9ddddb0e,0xd12ecb80,0x0f3a600b,
    0x0019bd30,0x073bbb22,0x17bdb730,0x00337a60,0x00000000,0x00000000,
    0x00000000,0x00000000,0x4d3ea9f5,0x00154004,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x20000000,0x009f54fa,
    0x77777400,0xeeeeeeee,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x4c000000,0x0007d33e,0x77777740,0x0eeeeeee,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x54400000,0x2a20001a,0x0154c01a,0x54400b20,
    0xaaa98000,0x99953100,0x032e0059,0x10053066,0x22004177,0x04c801aa,
    0x01aa8800,0x500d5440,0x51017997,0x51000035,0x0bb88035,0x00d54402,
    0x0007fea0,0xf500ffa2,0x3e6009ff,0x3fef9803,0xfffff700,0xffffb89f,
    0xf8804fff,0x3e0ff886,0x3ffe202f,0x5404ebcf,0x0fec01ff,0x07fd1000,
    0x103fe880,0x05fffffd,0x80007fea,0x7c403fe8,0x04ebcfff,0x88003ff5,
    0x3a2001fe,0x46fdc01f,0x7dc404fb,0x6baec00b,0xabcffd80,0x3fff24ec,
    0x804fb9df,0x41dd03fb,0x236600fd,0x800effc8,0x3e601fe8,0x3fd10005,
    0x01fe8800,0x008833f6,0x20007fa2,0xd9801fe8,0x00effc88,0x00007fa2,
    0x00000000,0x9fffffd5,0x036cf640,0x98407bee,0xf54fffff,0x001fd009,
    0x00020000,0x0007f400,0x44000000,0x0000007f,0x00200000,0x40153000,
    0xa802a62a,0x2a802a62,0x33fb7ff2,0x3bfa204d,0x007fe201,0x53fffff2,
    0x27d404fa,0x40015540,0x553002aa,0x50555555,0x01aa807f,0x554c1544,
    0xf82aaaaa,0x2aa8002f,0x802aa800,0x50aa02a9,0x55555555,0xf102fd81,
    0xf8817ecf,0x7c40bf67,0x1f61ff37,0x5c02aa80,0xfff9005f,0x013ea9ff,
    0xff8803fb,0x3fe2002f,0xfffc802f,0xdf07ffff,0x6406fb80,0xffffc85f,
    0xffb87fff,0x3ffe2003,0x3ffe2002,0x41ffe402,0x7fffc6f8,0x6c0fffff,
    0x6cff102f,0x6cff102f,0x2eff102f,0x4401ba5f,0x3f202fff,0xffff7003,
    0x8813ea9f,0xfffa805f,0x3ffea004,0xaacfc804,0x5f902aaa,0xf103ff80,
    0x559f901f,0xff985555,0xfa802eff,0x3ea004ff,0x7fe404ff,0x5546f886,
    0x0aaadfda,0x7f8817ec,0x3fc40bf6,0x5fe205fb,0x4017e7fa,0x3604fffa,
    0xfff3002f,0x813ea9ff,0xafd802fc,0x2bf6007f,0x03fc807f,0x2a02fc40,
    0x04fd80ff,0x3fa007f9,0x404ffd89,0x2007fafd,0x6407fafd,0x37c42fef,
    0xfd809f70,0x7ecff102,0x7ecff102,0x3e2ff102,0xb004faef,0x7f40ff5f,
    0x3fff2002,0x7c09f54f,0xfd7f8807,0x3aff1003,0x01fe401f,0x3a00fec0,
    0x807fcc4f,0x5f9803fc,0xf8827fd4,0xf1003fd7,0xfc807faf,0x037c46fb,
    0x2fd809f7,0x17ecff10,0x0bf67f88,0xffd33fc4,0x7f88019f,0x0bfa03fd,
    0x29ffd500,0x07f504fa,0x13ee9f50,0x27dd3ea0,0x2000ff20,0x7fcc04fa,
    0xfc80ff60,0x886f9803,0x74fa81ff,0x29f5009f,0x2bf204fb,0x81be22fd,
    0x17ec04fb,0x0bf67f88,0x05fb3fc4,0x3fae1fe2,0x53ea03ff,0x07fb04fb,
    0x4fa84c00,0xfb001fd0,0x3601fe65,0xc80ff32f,0x1fd0003f,0xdf34fd80,
    0xf003fc80,0xb07f905f,0x201fe65f,0x40ff32fd,0x226faafc,0x013ee06f,
    0x9fe205fb,0x4ff102fd,0x0ff102fd,0x417ff7ee,0x20ff32fd,0x500006fb,
    0x00bf309f,0x01fe8ff1,0x03fd1fe2,0x777777e4,0x01fdc04e,0x0bf63fe2,
    0xdddddf90,0x43ffa89d,0x23fc43fb,0x1fe201fe,0x4bf203fd,0x40df11fe,
    0x17ec04fb,0x0bf67f88,0x05fb3fc4,0x9be41fe2,0x23fc43ff,0x2ff881fe,
    0x84fa8000,0x5fa801fc,0xbf5027e4,0x3f204fc8,0x06ffffff,0x7e4037c4,
    0xffc806fe,0xa86fffff,0x0ff89eff,0x13f22fd4,0x27e45fa8,0x2bf52fc8,
    0x13ee06f8,0x3e205fb0,0x7c40bf67,0x7c40bf67,0x2fdcdb07,0x09f917ea,
    0x0620bff2,0x3e227d40,0x985fb006,0x30bf607f,0x01fe40ff,0x8803f900,
    0xfc801fff,0x7fec4003,0x42fd82ff,0x0bf607f9,0x8bf20ff3,0x206f89ff,
    0x17ec04fb,0x0bf67f88,0x05fb3fc4,0xb2f41fe2,0x4c2fd89f,0x3fff607f,
    0x5004fedd,0x802fb89f,0x99999ff8,0x9ff881ff,0x01ff9999,0x4c0007f9,
    0x05fb805f,0x20007f90,0xff886fe9,0x1ff99999,0x9999ff88,0x2fc81ff9,
    0x81be33ee,0x1fe404fb,0x0ff25fa8,0x07f92fd4,0x9f04d7ea,0x7c43ff71,
    0xff99999f,0x3fffaa01,0x7f9002ce,0xff5007e8,0x9fffffff,0xffffff50,
    0x3f209fff,0x07f40003,0x64013ee0,0x3a20003f,0x7fffd42f,0xa84fffff,
    0xffffffff,0x222fc84f,0x2e06f9ff,0x827dc04f,0x413ee4fc,0x413ee4fc,
    0xfdffb4fc,0xfa87ffff,0xffffffff,0x0077c404,0x9f517f40,0x3337f600,
    0xd86fdccc,0xdcccccdf,0x007f906f,0x5c04fa80,0x07f9004f,0x6c2fe800,
    0xdcccccdf,0xccdfd86f,0xc86fdccc,0x37e7e42f,0xf9809f70,0x30ffcc1f,
    0x1ff983ff,0xff307fe6,0x3fffb6a3,0xcdfd80ce,0x06fdcccc,0x37601fd4,
    0x6c6fd989,0x0ff8800f,0xff887fe0,0x3207fe00,0x7f80003f,0xc8027dc0,
    0x4c15003f,0x3fe20ffb,0xf887fe00,0x907fe00f,0x6fff885f,0x32013ee0,
    0x4ffecdff,0xfecdffc8,0xcdffc84f,0x3f704ffe,0xf007fc40,0x00fe403f,
    0xdfffffb1,0xa802fcc3,0x527ec05f,0x84fd80bf,0xeeeeeefc,0x80bee006,
    0xdf9004fb,0xf8dddddd,0x41ffffff,0x27ec05fa,0x4fd80bf5,0x7fe417e4,
    0x7ff776c6,0xfd700eee,0x75c05dff,0x2e02efff,0x202efffe,0x17ea00fc,
    0x14c09fb0,0x82cdba80,0x7fb001ca,0x3f66fa80,0x6437d403,0x7fffffff,
    0xb80f2200,0xfff9004f,0xcb8fffff,0x7fb02cdd,0x3f66fa80,0x3237d403,
    0x46ff882f,0xffffffff,0x8001800f,0x90018001,0x403fd80b,0x000006fa,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x0d544000,0x2600aa60,0x54c014c1,0x14c19802,0x400154c0,
    0xcb9802c9,0x0332e02c,0x30a60f22,0x00332a05,0x4000b260,0x0cca83cc,
    0x01e64000,0x2e200b26,0x6644020b,0x00cca801,0xe8803ca8,0x7ffd403f,
    0xf83fe204,0x3ffea02f,0xf83fe204,0x3ffea02f,0x3f7ee004,0x3ffff604,
    0x7f7ec0ef,0x220fe40f,0x05ff11ff,0xa8003bee,0x6c003fff,0x03bee05f,
    0x202fec00,0x2203fffa,0x4eacffff,0x9d95ff70,0x9003bee0,0x1fe880bf,
    0x7dc6fdc0,0xfd83ba04,0xf71bf700,0xfb077409,0x2e37ee01,0x2a7d004f,
    0x261bf907,0x54fea4fd,0x2213ea3f,0x807fa0ff,0xfa800ef9,0xb003fc8d,
    0x1df3007f,0x403fd800,0x03fc8dfa,0xdffb31d3,0xfffdb881,0x3be603ef,
    0x0017f200,0x00000000,0x00000000,0x7b8dd800,0x6f981ff0,0x8a7c47ee,
    0x020200ee,0x22002620,0x4c400cc1,0x00262000,0x18800988,0x011000cc,
    0x0ffb7fe2,0xf9004c40,0x5555550b,0xaa981555,0x982aaaaa,0x2aaaaaaa,
    0xaaaaaaa8,0x555540aa,0x200aaaaa,0x7cc002aa,0x86f882ff,0x44bee5fa,
    0x0005f94f,0x00000000,0x00000000,0x00000000,0x001ff982,0xff0bf900,
    0x1fffffff,0xffffffc8,0xffffc87f,0xfff87fff,0x40ffffff,0xffffffff,
    0x5fff100f,0x26013000,0x30ffd45f,0xf33fb3bf,0x9dfb7009,0xcefdb801,
    0x677edc00,0x677edc00,0xdfdb7100,0x3b6e203b,0xb7101def,0x2203bdfd,
    0x01defedb,0x01bffb2a,0x7039dfb7,0xfb5550bf,0xfc81555b,0x82aaaaac,
    0xaaaaacfc,0xdfdaaa82,0x55540aaa,0x00aaadfd,0x1009fff5,0x83bdfdb7,
    0x07bee5f9,0x7f4fffee,0x76f7ec00,0xbdfb02ff,0x3f605ffd,0xb02ffede,
    0x05ffdbdf,0xfffddff7,0xfddff705,0xdff705ff,0xf705fffd,0x05fffddf,
    0x5ffddffb,0xffdffd30,0x404fb87f,0x1fe404fb,0x0007f900,0xfb8013ee,
    0xff5fb004,0xfddff700,0x8afcc5ff,0x426200ff,0x27e402fc,0x9f903fea,
    0x7e40ffa8,0x3207fd44,0x207fd44f,0x43fdc419,0x43fdc419,0x43fdc419,
    0x23fdc419,0x1bea0dfc,0x3ff513fa,0x3ee027dc,0x001fe404,0x2e0007f9,
    0x13ee004f,0x0ff5fe20,0xff710660,0x07f8afcc,0xf1017e60,0xf88ff20d,
    0x7c47f906,0x7c47f906,0x4007f906,0x3fe000ff,0x003fe000,0x0df30ff8,
    0x05fa87fa,0x027dcbf9,0x7f9013ee,0x001fe400,0x2e004fb8,0x74fa804f,
    0x7fc0009f,0x27fcbf30,0x5400fe80,0x549f504f,0x549f504f,0x549f504f,
    0x009f504f,0xfb000fec,0x00fec003,0x0fee3fb0,0x05f927e4,0x04fb9be2,
    0x3f2027dc,0x00ff2003,0x70027dc0,0x25fb009f,0x360007f9,0x7ccbf31f,
    0x4bee00df,0x77dc1dec,0x5feeeeee,0x3bbbbbee,0x3bee5fee,0x5feeeeee,
    0x3bbbbbee,0xec985fee,0x981ffffe,0x1ffffeec,0xfffeec98,0xfeec981f,
    0x05fb1fff,0x01fe97ea,0x403fa9fe,0x77e404fb,0xc84eeeee,0x4eeeeeef,
    0x70027dc0,0x47f8809f,0x764c01fe,0xf31ffffe,0x80efe98b,0xffbfd5f8,
    0x77777e43,0x3f24eeee,0xeeeeeeee,0x3bbbbf24,0x3f24eeee,0xeeeeeeee,
    0x6677fdc4,0x7fdc1ffc,0x41ffccce,0xfccceffb,0x677fdc1f,0x3fb1ffcc,
    0x1fe97ea0,0x03fa9fe0,0x3f2027dc,0x86ffffff,0xfffffffc,0x0027dc06,
    0x5fa809f7,0xff7027e4,0x23ff999d,0x4fe885f9,0x33f98fe8,0x002fdc9f,
    0x7dc00bf7,0x017ee005,0xfd81ff88,0x3607fe21,0x207fe21f,0x07fe21fd,
    0x817e47f6,0x40bf64fb,0x007266f8,0x3fc809f7,0x000ff200,0xf70027dc,
    0x4c2fd809,0x03ff107f,0x417e63fb,0xb9fdc6f9,0xffa97e1f,0x01ff5000,
    0x4003fea0,0xf5000ffa,0xfa87fa0b,0x7d43fd05,0x7d43fd05,0x3ee3fd05,
    0x7e45fb05,0x000bf704,0x3fc809f7,0x000ff200,0xf70027dc,0x4cffc409,
    0xa81ff999,0x263fd05f,0x44df305f,0x7c4bea6f,0x80067f44,0xfd000cfe,
    0x33fa0019,0x446fa800,0x1bea1ffd,0x7d43ffb1,0x50ffec46,0x1ffd88df,
    0x7fa85ff1,0x3e60ffc4,0x700faa1f,0x03fc809f,0x4000ff20,0x3ee004fb,
    0x3fffea04,0xa84fffff,0x8ffec46f,0xfb9935f9,0xf10fec5f,0xf983fb7d,
    0x0eccbdff,0x65efffcc,0x7ffcc0ec,0x4c0eccbd,0xeccbdfff,0x373bfe20,
    0x3e21feff,0xfeffdcef,0x373bfe21,0x3e21feff,0xfeffdcef,0x3737fee1,
    0xdffb82ff,0x7fc3ffdc,0x2027dc07,0x3f2003fc,0x09f70003,0xfb027dc0,
    0xfb99999b,0xb9dff10d,0x3e63fdff,0x45dfff35,0xfffa83fa,0xffffb102,
    0xffb101df,0xb101dfff,0x01dfffff,0xdfffffb1,0xdfffe981,0x7f4c1fc8,
    0x41fc8dff,0xc8dfffe9,0x7fff4c1f,0x7541fc8d,0x2a01efff,0xc81efffe,
    0x027dc05f,0xfc800ff2,0x09f70003,0xf8827dc0,0x207fe00f,0xc8dfffe9,
    0x0002201f,0x02620008,0x20009880,0x26200098,0x40008800,0x00440008,
    0x06000220,0x40600600,0xeeffeeed,0x7777e40e,0xefc86eee,0xd86eeeee,
    0xeeeffeee,0x7ff776c0,0x0bf50eee,0x02204fd8,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0xffffff80,0x7fe40fff,0xc87fffff,
    0x7fffffff,0xfffffff8,0x7fffc0ff,0xfb0fffff,0x006fa807,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x40f32000,0xca814c29,0x055cc00c,0x203cc800,0x98014c29,0x0bb8802c,
    0x40044002,0x298003c8,0x0310014c,0x8000b260,0x157300cb,0x76c17a20,
    0x00f32000,0x32a0aa62,0x027d400c,0xf882fec0,0x205ff11f,0x3ee00efb,
    0x36001eff,0x47fe205f,0x7d402ff8,0x3fe203ff,0x204eacff,0x203ffffa,
    0x7c4006f8,0x005ff11f,0x3fea01f6,0x3fb0003f,0x4fffff98,0x1fd06f88,
    0x8817f600,0x706ffffb,0x7ff401df,0x80ff6000,0x07fa0ff8,0x5100ef98,
    0xb005ffd7,0x0ff8807f,0xdfa807fa,0x1d303fc8,0x201dffb3,0x2ffdcffa,
    0x8800df10,0x007fa0ff,0x37ea02fc,0xd8003fc8,0x26ffea1f,0x37c44feb,
    0xfd800fe8,0x37ffe603,0x3be602ac,0x400bf700,0x10100098,0x20013100,
    0x26200ffb,0x00202000,0x20019831,0x717ec008,0x01be20bf,0xb7004040,
    0x18807fff,0x7ec000cc,0xff10bfa1,0xfe837c41,0x1004c400,0x310007ff,
    0x00000001,0x20000000,0x000004fd,0x00000000,0x6f983fe0,0x0000df10,
    0xfeffe980,0x000001ff,0x17ea3fb0,0x0df127dc,0x200003fa,0x000003fc,
    0x05e88026,0x82f443d9,0x417a21ec,0x17ee01ec,0x00e77edc,0x80e77edc,
    0x03d905e8,0x8073bf6e,0x413ee2fe,0x7ddb36f8,0x3bfb6e20,0xf13fe81d,
    0x6dc03ffd,0x65401cef,0xf91ffdee,0x44dfd305,0x701fd06f,0x7c0bdddd,
    0x7775c007,0x407f305e,0x45fb06f8,0x45fb06f8,0x05fb06f8,0x3fa61fdc,
    0x303fffef,0x7fffdffd,0x3f60df10,0x7f7ff4c2,0x2df903ff,0xdf100ffa,
    0x0bffdff5,0xfffddff7,0x5f52fdc5,0xffd309f7,0xb107fffd,0x3fffdfff,
    0xfff707f6,0xfe837c4f,0x7ffffc80,0x2aa2bf30,0xffff9009,0x8813ea0f,
    0x445fb06f,0x445fb06f,0x205fb06f,0x27f40ff9,0x3fa07fea,0x220ffd44,
    0xe85fb06f,0x40ffd44f,0x01efeff8,0x4c33ffe2,0x220cc1ff,0xd8bf27fb,
    0x9fd0bf17,0x7e41ffa8,0xfe8ff42d,0xff3dfb10,0x0fe837c4,0xfa83fc40,
    0x2fffffee,0xfa83fc40,0x6c1be204,0x6c1be22f,0x6c1be22f,0x3fffe62f,
    0xfc82fd44,0xfc82fd45,0xfd837c45,0x7e417ea2,0x00bffb05,0x9f709ff1,
    0xfe87fc00,0x547f93e0,0x44bf905f,0x3e3fb07f,0xf0dff98f,0x303fe21f,
    0x7f8803ff,0x537bff70,0x7c403ff9,0x4409f507,0x445fb06f,0x445fb06f,
    0x4c5fb06f,0x05f902ef,0x05f91be2,0x0df11be2,0x02fc8bf6,0xefe88df1,
    0x88ff11ff,0x00bf307f,0x50fe8fec,0x3f23fc5f,0x7dcdf102,0x3fa3fb04,
    0x13fc3ffc,0x7ff445ff,0xb83fc401,0x40bf904f,0x09f507f8,0x2fd837c4,
    0x17ec1be2,0x8bf60df1,0x07fa04f9,0x00ff47f8,0xb06f88ff,0xf00ff45f,
    0xdfb4fd8f,0x6f88df31,0xd930df30,0x323ffffd,0x37c4fb2f,0x27f807fa,
    0x23fb03fc,0x7c41effd,0x337ffe27,0x202efbef,0x09f707f8,0x7f881be6,
    0x7c40bf50,0x7c45fb06,0x7c45fb06,0x7cc5fb06,0xf807fa04,0xff00ff47,
    0x5fb06f88,0x4ff00ff4,0x56ff47f8,0x9837c45f,0x677fdc6f,0x9f51ffcc,
    0x745fa97e,0xfd9fe01f,0x3f23fb02,0x225fa80d,0xb0dffeef,0x83fc409f,
    0x0ff105fa,0x5fb83fc4,0x7ec1be20,0x7ec1be22,0x7ec1be22,0xfd80b222,
    0xfd8df102,0xf88df102,0x7ec5fb06,0x7ccdf102,0x17fffc46,0x2fd41be2,
    0x3fb03ff1,0x44bf3fe2,0x817ec1fe,0x413f26f8,0x20df31fe,0x45be22fd,
    0x07f88000,0x17ea0ff1,0xbf707f88,0x7c40ff80,0x2207fc2f,0x207fc2ff,
    0x64002ff8,0xc8bf704f,0xf0bf704f,0x22ff881f,0x4bf704fc,0xfffa87f9,
    0xfc837c40,0x7f417ea3,0x373ffea1,0x04fc84ff,0x42fdcbf7,0x0ffa1ffb,
    0x06f88ff5,0xb03fc400,0x02fe889f,0x2fdc1fe2,0xfe88bfe0,0xd117fc2f,
    0x22ff85ff,0x3a62ffe8,0x307fe204,0x1ff883ff,0x7fc0ffcc,0x88bffa22,
    0x0ffcc1ff,0xffd50ffe,0xfa86f883,0x36237d46,0x7ffd41ff,0x3ff102ef,
    0x3e21ff98,0x87ffea1f,0xffdcdff9,0x00037c41,0xff981fe2,0x404fecbd,
    0x0bf707f8,0xefcdffb8,0x6ffdc2fd,0x5c2fdefc,0xfdefcdff,0xb803ff62,
    0x3ffdcdff,0xfb9bff70,0x37fee07f,0x5c2fdefc,0x3ffdcdff,0xffceffa8,
    0x3e20efef,0x1ffdccef,0x7ee77fc4,0x5fd41fef,0x9bff7001,0xffb07ffb,
    0x83f99fdb,0x81dfffd8,0xcc8006f8,0x1cccffcc,0x177ffec4,0xcffcccc8,
    0x00df71cc,0xf71bffd5,0x1bffd505,0xffd505f7,0x7dc5f71b,0xfffea806,
    0x7ff5401e,0x7f5401ef,0xa82fb8df,0x201efffe,0xd1dfffea,0xfffdb8bf,
    0xffd300de,0xd83f91bf,0xffea8007,0x3ff201ef,0x0c03f93e,0x20017a20,
    0xffffffff,0x3e00603f,0xffffffff,0x4400bd73,0x00044000,0x00020022,
    0x000c0006,0x00c00044,0x01000040,0x3c800440,0x2000c000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x55530000,0x53035555,0x54c00557,
    0xba9800aa,0x2aaaa60a,0x2a600aaa,0x0032e009,0x32205950,0x020bb883,
    0x3c881654,0x6440736e,0x09999953,0x80357510,0x209aca98,0x20aa00a9,
    0x2e014c29,0x0164c03f,0x1dd127dc,0x64c076c0,0xfffffc82,0x7ffe44ff,
    0x3ee03fff,0x904fffff,0x2bffffff,0xfffffffc,0xfffffb81,0x000fe80d,
    0x320bffd3,0x7fffc41f,0x3fa64eac,0x6c3f905f,0x1fc80ffd,0x40fffff9,
    0xefffffc8,0xfffffc81,0x440bf65f,0x88ffc47f,0x1ffe02ff,0x203fffa8,
    0x3f62fff8,0x3a0df504,0x567e40ff,0x5dc1aaaa,0x81ffdb9a,0xecabcffd,
    0x5e7ff444,0x55535dca,0xfd83fd55,0x0efc989c,0xea8007f4,0x84fa85fa,
    0xeffd98e9,0x217ebaa0,0x83fb04fa,0x266624fa,0x2fbf607f,0x360ffda9,
    0x3baabcef,0x3fc40bf6,0x1fe83fe2,0x7d413f60,0x3e03fc8d,0x41fea1ff,
    0x1ffd03fd,0x40001fc8,0x0f7dc4fe,0x8077ec08,0xf70ff400,0xfd17ea07,
    0x45f88001,0x11000ee8,0x3a22fc40,0x22ffe40e,0xff100ee8,0x3e03fe20,
    0x003ff32f,0x1fe205fb,0x20000404,0x9300cc18,0xf885fd05,0x9035100f,
    0xfb80003f,0x4007fe25,0x20000ffa,0x4cdf11fe,0x743f91bb,0x2fc4000f,
    0x80000bf2,0x417e45f8,0x3f23fda9,0x441fe202,0x2e5fb06f,0x05fb005f,
    0x80001fe2,0x00000098,0x5fa8bf70,0x003f9000,0x3ee2fc80,0x00ff6005,
    0x3ee3fd00,0x22ffff52,0x3603fa4f,0x265f884e,0x2a9d104f,0xbf101cfd,
    0xc99827cc,0x8809f34f,0x10bfa07f,0x007fd4ff,0x7f8817ec,0x02f7775c,
    0xeeb82fb8,0x440005ee,0x70bf60ff,0xf90bdddd,0xf3000335,0x001fe41d,
    0xe80003fd,0xdf11f91f,0x3fa5f823,0x44077ec0,0x4403fa5f,0xffeefcdf,
    0x3fa5f884,0x21dfff00,0x3fc400fe,0xf519ff50,0x177f447f,0x7c40bf60,
    0x3ffffe47,0xfc82fb80,0x80007fff,0x90ff13fd,0xf90fffff,0x205bffff,
    0xd85feee8,0x83fe002f,0x403cccc9,0x3eafb1fe,0x07f4fb03,0x90980bfb,
    0x7ffc405f,0x2603fea3,0x4cc05f90,0x2200bf20,0x7ffcc07f,0xffe981ff,
    0x02fd80be,0x3fc40ff1,0x2017e440,0xa80007f8,0x8809f76f,0xdccca87f,
    0xff982fff,0x3fa0cfff,0xa8ff1002,0x7406ffff,0x0beadd1f,0x361fd3ec,
    0x17e6005f,0xff07ff10,0x002fcc03,0x7c4017e6,0x7ffec407,0x3ffae03f,
    0x8817ec3f,0x81fe207f,0x403fffd9,0x2da807f8,0x07fa7fe0,0x6400ff10,
    0x6fd9807f,0x7c400bf6,0x37d4cc47,0x8bec7fa0,0x7f4dd04f,0x74005fd8,
    0x83fc400f,0x03fa02fd,0x2001fd00,0x3fa607f8,0x405ffc8c,0x3f65ffda,
    0x440ff102,0x273fa07f,0x03fc4009,0xfc80fffc,0x7f8806fd,0x007fe200,
    0x03fc97f4,0x7cc03fe0,0xfc8ff406,0x7c737fd0,0x01bf7fa5,0x33265f70,
    0xfd837c42,0xd915f702,0x997dc05b,0x0ff102cc,0x3fe617fc,0xb2ffa802,
    0x81fe205f,0x0bf307f8,0xe807f880,0xfff103ff,0x00ff1007,0xf9002fe8,
    0xe801bee7,0x80df303f,0x267f51fe,0x47f37ffd,0x002ffafe,0x27ff4bf1,
    0x17ec1be2,0xecfaafc4,0x3a5f881f,0x0ff104ff,0x2fe417e6,0x7f94fc80,
    0xf8817ea0,0x8009f707,0xff9807f8,0x401ff603,0x3e2007f8,0x23fd000f,
    0xf5002ff8,0x40df301f,0x11fa0ff9,0x1fd07ec1,0xfd005ff3,0x113eff21,
    0x20bf60df,0x17dc20fe,0xfbfc87f4,0x2e0ff104,0x00bf704f,0x827dcff2,
    0x1fe204fc,0x220037dc,0x03ff007f,0xf8801fec,0x09fb1007,0xff91bee0,
    0xefe83105,0x20bb7cc0,0x77d46fc8,0x7f43fc80,0x5c03ff50,0x9f39f33f,
    0x5fb06f88,0xfe883fb8,0xcf99fdc0,0x0ff104f9,0x3fa03fea,0x8ffcc013,
    0x7fcc1ff9,0x441fe201,0x3e2003ff,0x206fb807,0xf1000ffa,0xfb999b0f,
    0xb999b0bf,0xfd881fff,0x44feddff,0xecdeffe8,0xffbdfd6f,0x59df703f,
    0x1fd09fd7,0x7c40ffdc,0x3bfbbf66,0x7ec1be23,0x3e61be22,0xfb37c41e,
    0xf107dfdd,0x667ff40f,0x3bf66ffc,0x44ffedcd,0xffecdffc,0x703fc404,
    0x88013bff,0x3bfb207f,0x003ff500,0x7ff43fc4,0xfff02dff,0xea807dff,
    0x702cefff,0x25bffffd,0x00dfffeb,0x0b7fff66,0x3ff207f4,0xdd90fec0,
    0x37c47dfd,0x07f62fd8,0x6c357ff5,0x3fbbb21f,0xff99993e,0x7dc43999,
    0x2e0bffff,0x2dffffff,0x5dfffd70,0x3ff33320,0x7fd41ccc,0x666644ff,
    0x3e1cccff,0xffff983d,0xfcccc803,0x1331cccf,0x00026600,0x00cc0004,
    0x00100011,0x1dfb01fd,0x4f980fea,0x2fd837c4,0xfff707f5,0x980fea9f,
    0x3ffffe4f,0x3103ffff,0x00133001,0xffff8018,0x503fffff,0xffff87b9,
    0x003fffff,0x20019bd3,0xffffffff,0x0000003f,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x02ae6200,
    0xdddfd910,0xdd9501dd,0x0f223b9b,0x00014400,0x22179995,0x07ddb34e,
    0x23deedb8,0x0d4c02a8,0x55440055,0x4c40009a,0x551001ab,0x2aa35555,
    0xaaaaaaaa,0x5555530a,0x5554c555,0xaaaa8009,0x57510009,0x00aaa001,
    0xff5013ee,0xf101bfff,0xddffd9bf,0xfeeffd81,0x00df11ff,0x22001fe0,
    0x12fffffe,0xffdff5bf,0xddffd30b,0x103fe8ff,0x01fe21ff,0xffffffa8,
    0x7ffd401d,0x7e401eff,0xdf4fffff,0xdddddddd,0x3ffff21f,0x3ff67fff,
    0xf00dffff,0x07ffffff,0x1fffffe4,0x80bffe20,0xf702fff8,0x1dfd759f,
    0x27e43fd8,0x3fa06fe4,0x2000df11,0xdfd8007f,0x3fe21312,0x83ff30cf,
    0x26140dfe,0x23fd80ff,0x3ea007f8,0x3ffecbae,0x9339ff30,0xefef805f,
    0x021f1aaa,0x559f90f8,0x67ec5555,0x82ffecba,0xffecabff,0xa9adfd83,
    0x3fea03ff,0x0fffc04f,0x3a20ffc4,0x7c43fc3f,0x6c07fc47,0x000df11f,
    0x3fe001fe,0x213fe201,0x017f24fb,0x37cc4fd8,0x3bffffe2,0xb85fa81d,
    0x83fd80ff,0x2fdfd400,0x85dfd0f8,0xb007f90f,0x81ff705f,0x547fc87f,
    0x206f986f,0x4c07fafd,0x209f902c,0x21fe27fa,0x837dc7f8,0x00df11fd,
    0x3bffbba6,0xf301eeee,0x307f880d,0x000ff4bf,0x17f43ff1,0x3b733fe2,
    0x82fd43ff,0x00fe85fc,0x05f9fe80,0xf27dc41f,0x3600ff21,0xf8bfb02f,
    0x320ff887,0x105fb03f,0x0007faff,0x3fe01ff8,0xbf70bfa1,0x3fb04fc8,
    0x67ed5be2,0x7ffffcc1,0x302fffff,0x06f880bf,0x007fcdf3,0x2fd57ee0,
    0x7fd43fc4,0x7cc17ea1,0x98007f87,0x1f05f8cf,0x643e1f50,0x02fd803f,
    0x887f8ff3,0xb817ec7f,0xf74fa83f,0x03fc0009,0xcffa8bf6,0x7ec0ffda,
    0x3e23fb02,0x4ffeefce,0xf3001fe0,0x306f880b,0x001fe2df,0x407f57f4,
    0x49f907f8,0x03fe05fa,0x3f9000ff,0x203e0bf1,0x3f21f1f9,0x202fd803,
    0x321fe0ff,0xb81fec5f,0xf32fd84f,0x1be6000f,0x6ff47fb0,0xfc80dfff,
    0x3e23fd03,0x03fea4ff,0x7ffc03fc,0x7fffffff,0x27d41be2,0xf50001fd,
    0x1fe207ff,0xffeeb7d4,0xa8ffc1ee,0x2aaaaffa,0xeff8b7c0,0x4cc1f1ee,
    0x3f21f0fd,0x24eeeeee,0x87fe02fd,0xefecbbff,0x64077d40,0x3a3fc45f,
    0x6f98001f,0x4f99fe40,0x709f7002,0x0ffe23ff,0x03fc07fe,0x67fee664,
    0x1be24ccc,0x07f71fe4,0x881bf600,0x3ebf707f,0x742fffff,0xffffff1f,
    0x3ee01fff,0x3dddff13,0x06f7c43e,0x3ffff21f,0x0bf66fff,0x3ffe1fe8,
    0xfd00bfff,0x9fffb99f,0x27e45fa8,0x0ff30150,0x3df52fd8,0xa83fe200,
    0x0ff11fff,0x03fc0bf6,0xf1017e60,0x4c6fb88d,0x74040bff,0xeeeffeee,
    0x7f41fe21,0xff817ea3,0x333ff331,0x887f4033,0x3e21f05f,0x07f90f80,
    0x7fc05fb0,0x3f267fe1,0x3ffb200e,0x7ec3fccf,0xfe83fcc2,0x203fc40f,
    0xfffd11fe,0xfb05bfff,0x3fb9fd9f,0x17ec1be2,0x7cc007f8,0x677fc405,
    0xfc81fffc,0xe87ecdff,0xeeeffeee,0xf931fe21,0x882fd41f,0x003fc0ff,
    0x82fc4bf3,0x43e03a0f,0x2fd803fc,0x3fc1ff10,0x320013f2,0x267fe22f,
    0x441ff999,0x1ff82fff,0x7e41ff10,0x4fffeeed,0xfb3effc8,0x7ec1be23,
    0x9800ff02,0x7ffc405f,0x2e00dfff,0x405ffffe,0x3fe204fb,0x41efffee,
    0x0df705fa,0x7fe400ff,0x1f05ffff,0x7f90f804,0x2e05fb00,0x3e23fc6f,
    0x07f4000f,0xfffffff5,0x1dfb09ff,0x3ee09f90,0x7dc17ee5,0x23fb0207,
    0x05fb06f8,0xf98007fa,0x08b7c405,0x803be200,0x99dfc999,0x77fffc41,
    0x10bf503d,0x00ff07ff,0x3bbbbfe2,0x3ea1f05f,0x07f90f82,0xff105fb0,
    0x4fc87f85,0xfb0ff200,0xfb99999b,0xff88060d,0xfb07fd43,0x003fe205,
    0x06f88fec,0x13f605fb,0x4405f980,0x7f50006f,0xffffff80,0x1fe21fff,
    0x7545fa80,0x200ff06f,0x82fc43fb,0x1f03d30f,0x3f600ff2,0x7c2ffd42,
    0x400ff987,0x7fc46fd9,0x0007fe00,0x3fb3bfee,0xc837ec3f,0x47f6005f,
    0x05fb06f8,0x3733ffe6,0x880bf301,0x3f90006f,0xdfdaaa80,0x1fe20aaa,
    0xfeeffa80,0x7f6c0dff,0x3eeeeeef,0x9ff103fa,0x2003e599,0xddddf90f,
    0x777ecddd,0xff05fffe,0xddb13f60,0xfa819fff,0x0027ec05,0x1dfffea8,
    0xeeefff98,0x36000eff,0x360df11f,0x3ffaa02f,0x0bf301ff,0x30006f88,
    0x04fb8005,0xfa801fe2,0xf01cefff,0xffffffff,0x2217e69f,0xff5fffff,
    0xffffffff,0x3ffff21f,0x3ff67fff,0x3e01ceef,0x741ff307,0xfb01cdef,
    0x006fa807,0xeb880180,0x8002ceee,0x20df11ec,0x013002fd,0x74405f98,
    0x00000005,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0xaaaa8000,0xaaa98099,0x31aaaaaa,0x55555555,0x260aaa00,
    0x2055100a,0x2aa0d42a,0x0aaaaaaa,0x0aa60551,0x40335555,0x455302a9,
    0xaaaaaaa9,0x803551aa,0x02aa22a8,0x03530aa8,0x01551a88,0x0154c550,
    0x2aaaaa55,0x00aaaaaa,0x751002aa,0x80551015,0xfffffff8,0x3ffff20c,
    0xf95fffff,0x0fffffff,0xfb0fff70,0x7c1be205,0xfff0fe65,0x21ffffff,
    0x2ff886f8,0x3fffffe2,0x07fec0bf,0xfff737fc,0x2bffffff,0x2fe406fb,
    0x7fd413fa,0xf9807f50,0xfa809fb4,0x220fff26,0xffffff6f,0x501fffff,
    0x36203ffd,0x4c3fffff,0x57fc406f,0x2a5ffcba,0xdccccccc,0x555bf95f,
    0xff880555,0x102fd87f,0xfa93e0df,0x6fed5542,0x0df10aaa,0xaff887fd,
    0x6c5ffeba,0x3ffd43ff,0x99999993,0x81ffc9ff,0x7fcc0ff8,0xf517f441,
    0xf53f9809,0x323fc80f,0x56f886ff,0x55bfb555,0x7ff4c155,0x7bfd01ff,
    0x7cc3ff95,0x443fc406,0x3fd000ff,0x6c000ff2,0x2fd87fbf,0x9f10df10,
    0x9f700fdc,0x9fb1be20,0xff10ff10,0x3637f747,0xdf7007ee,0xfd80ffa8,
    0xfc8bf904,0x2a027cc5,0xf807fe3f,0x0bfbf21f,0x13ee0df1,0xff9dff98,
    0xbf905501,0x3e2037cc,0x3003fd07,0x001fe4df,0x43fc67d4,0x4df102fd,
    0xdaadfbaa,0x4fb81abf,0x2fdcdf10,0xbf907f88,0xf887e774,0xff8807dc,
    0x7cc4fe81,0x25ff100f,0x2fcc0ff9,0x4fd8bea0,0xbfc8df30,0xb837c46f,
    0xff0e404f,0x26f98003,0x3fc406f9,0x5fb007f8,0x22001fe4,0xd87f88ff,
    0x74df102f,0xffffffff,0x04fb85ff,0x03be6df1,0x6fa83fc4,0x7dcfe7ba,
    0x7ec00fd9,0x6c1ff304,0x47fd403f,0x45f883fe,0xfa8bee09,0xfc87f906,
    0x1be22fda,0x3e0027dc,0x37cc001f,0x7f880df3,0xf9804fc8,0x2001fe46,
    0xb0ff12fd,0x21be205f,0x701f61fb,0x23be209f,0x1fe201ff,0x3adf2fec,
    0x803f6dd6,0xa7ec06fa,0xdfb006f9,0xa9be20df,0xff07ee3f,0xfc81fd03,
    0x1be26faa,0x3e0027dc,0x2fdc001f,0xff880df3,0x002ffeee,0xcefc85fb,
    0xfa82cccc,0x3f61fe25,0xfeeeeeee,0x1ba0fc86,0x3e209f70,0x7c402fef,
    0x3e2ff887,0x367f5f95,0x03ff100f,0x2fd8ff88,0x03fff100,0x64df937c,
    0x2627ec1f,0xfd2fc86f,0x7dc1be23,0x00ffc004,0x7cc5fd10,0x7fffc406,
    0x4c02efff,0xffffc86f,0x0fe85fff,0x3ff61fe2,0x6fffffff,0x202fc7c8,
    0xfff104fb,0x9ff8805f,0x7c5ffdb9,0x321fff35,0x009fb01f,0x001bfbf2,
    0x37c01ffd,0x03f23fff,0x83fc8df5,0x22bf52fc,0x009f706f,0x36001ff8,
    0x2037cc5f,0x7fe447f8,0x320bf601,0x099999cf,0x1fe217e4,0x37c40bf6,
    0x7013e3ec,0x2bbe209f,0x3fe200ff,0x443fffff,0xfc97fa5f,0x2006fa81,
    0x2001fff8,0x3a05fffb,0x329f9f57,0x3a1ff80f,0x3e2fc80f,0xf706f89f,
    0x01ff8009,0xf981df90,0xc83fc406,0x20df305f,0xef9803fc,0x9ff99999,
    0x2205fb09,0xdffdd56f,0x20ddffdd,0x2df104fb,0xff100efb,0xf1013599,
    0x3f917dcb,0x4001ff88,0x3e6005fb,0xfd02ff9f,0x3edbe3f2,0x0df33fd8,
    0x8cfb8bf2,0x009f706f,0xfc801ff8,0x406f980e,0x0df507f8,0x1fe40ff6,
    0x7ffffdc0,0xb4ffffff,0x4dbe205f,0xfdccefcc,0x09f704cd,0x05fd9be2,
    0x3e600ff1,0x3617e405,0x4fb8004f,0x7dcffa00,0x5f8fd80f,0x2a0fb3f9,
    0x3207f76f,0x3e7fe22f,0x8009f706,0x77e401ff,0x880df300,0x309fb07f,
    0x01fe40ff,0x6666664c,0xfb2ccffc,0xf11be205,0x2e017d49,0x44df104f,
    0x07f883ff,0xfb809f30,0x0003bea2,0x7dc013ee,0x7e427f46,0xdd9f32fb,
    0x07f47fc0,0x67e42fc8,0x009f706f,0x3f201ff8,0x01be600e,0xffa88ff1,
    0x3203fd82,0x7c40003f,0xf102fd87,0x3ee4f88d,0x8827dc01,0x20ffcc6f,
    0x9f3007f8,0xff13fb80,0x13ee0003,0xf30bfe20,0x47efc83f,0x3f606eff,
    0x0bf206fc,0x2e0dfff1,0x0ffc004f,0x4c00efc8,0x77fc406f,0x983fffee,
    0x0ff200ff,0xb0ff1000,0x31be205f,0x6c07e47f,0xeeeffeee,0xff70df10,
    0xa803fc41,0xc9fdc04f,0xffffffff,0x013ee06f,0x37e417f6,0xff917fee,
    0x1fffd40b,0xff905f90,0xb013ee0d,0xdddffddd,0x3ffffe67,0xff36ffff,
    0x15dddddd,0x19dfffff,0xf900ff60,0x7f880007,0xdf102fd8,0x03f22fa8,
    0x3ffffffe,0x20df10ff,0x01fe26fd,0x3ee027d4,0xffffffb3,0x7dc0dfff,
    0x807fdc04,0x3fee4ff8,0x202ffcc2,0x3f200fff,0x706ff882,0xffff809f,
    0xf34fffff,0xffffffff,0x3ffffe6d,0x00003fff,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x80322000,
    0x8026200b,0x6c40001c,0xdb880000,0x2e01defe,0xe881cefd,0x6d43d905,
    0xdfd98cef,0x00054400,0x207bddb7,0x700cefdb,0xc83ddfdb,0x21bd505e,
    0xd52ed8bd,0xeeeed81b,0xaa986eee,0x2017540a,0x2e1d74e9,0x77441dfd,
    0x6c03b600,0x40df504f,0x7ff304fa,0x0ffe6000,0x7dc04fa8,0x42fffeef,
    0xfffeffe9,0xfd837c43,0x3bff7be2,0x7406fdef,0xffe9807f,0xefd87fee,
    0x7f42ffed,0x442feeef,0x17fc43ff,0xf3ffdb9f,0xfffe8bfd,0x7dc7ffff,
    0x3ea1ffff,0xfca7d404,0x1fffefc9,0x6fa81fec,0x32ebfe20,0x2a01ff9b,
    0x13fe604f,0x805ff700,0x20cc04fa,0x27f47fb8,0x6f887fea,0x36145fb0,
    0x405f90ff,0xdfe807fe,0x513f2140,0x417ee1ff,0x361ff980,0x5c77fc4f,
    0x200fd4ef,0xf70cc3fe,0x2e02fccb,0x45fdf93f,0x837d45fc,0x7fd403fd,
    0x403fffff,0x7f4404fa,0x1efc800d,0x0004fa80,0x82fd43fe,0x41be25fc,
    0x97ee02fd,0x072204fa,0xf100bf90,0x7e4ff20d,0xab7e4003,0xf52ff86f,
    0x32007ecf,0x37cc405f,0x9174cdf1,0x307ff23f,0x883ff0df,0x7fc400ff,
    0x402ffc9c,0xdfb004fa,0x03bf6201,0x00027d40,0x40bf23fb,0x41be26f8,
    0x8fea02fd,0xe80004f9,0x04fa801f,0x0bfee9f5,0x1ff9fd00,0xb27d4ff0,
    0x077d401f,0x37fff644,0x365fc9fe,0xd107f90f,0xfb89f90b,0x645fb804,
    0x7777645f,0x205eeeff,0x7f441ffb,0x5ccccc04,0x981999df,0x1ffffeec,
    0x13fc03fd,0x88bf60df,0xeeefeedb,0x266665fe,0x41999999,0xefb800ff,
    0x5feeeeee,0x0077fff6,0x7c0bffe2,0x0fd8fea7,0x3203ff30,0x746f99af,
    0x3f43ffa7,0xf88005f9,0x6c00ff47,0x363fcc2f,0xffffffff,0x8ffdc07f,
    0xff805ff8,0xffffffff,0x33bfee1f,0x3fd1ffcc,0x0df13fc0,0xcffe8bf6,
    0x2ccccefd,0x3ffffffe,0xff11ffff,0x3bbbf200,0x4c4eeeee,0x200efffd,
    0xff00ffe8,0x01fb1fd4,0x37c05fd1,0x98fd8df5,0x64df3fcf,0x2fd8002f,
    0x3f600df3,0x2a03fc42,0x3fe6004f,0x201ffd43,0xcefdcccc,0x3ff10ccc,
    0x0bf63fb0,0x0df137c4,0x52fd4bf6,0xcccc809f,0x0ccccccc,0x3ee003fe,
    0xfd510005,0x77f7ec0d,0x8fea7f80,0x04fd80fd,0x37ff6fec,0x3e3f27ee,
    0x017e4bf6,0x3fcafd40,0xf989fb00,0x8027d406,0xfd302ffb,0x027d4009,
    0x47fa0bf5,0x8bf704fc,0x97fc40ff,0x01bea2fc,0x01ff4000,0x00007fd4,
    0xdf701ff1,0xa9fe17f6,0xf703f63f,0x17b7100d,0x5ebfa877,0x7e49f5f9,
    0xd1ff0002,0x3bea001f,0xf500ff60,0x03df9009,0x800dfe88,0x1bea04fa,
    0x3e23ffb1,0x20ffcc1f,0x3ffa22ff,0x3fa27f72,0x0054001f,0x8102ffea,
    0x30000cfe,0x23ff30ff,0x53fc3ff8,0xf307ec7f,0x4c00001f,0xfbf32fdf,
    0x80017e47,0x2005fdfc,0xfffdfff8,0x1027d401,0x6c001dfb,0x27d400ef,
    0xfb9dff10,0x7fdc3fdf,0xb83ffdcd,0xfdefcdff,0x7dd9df12,0x403b99ff,
    0xffd806fd,0x7cc7ecdf,0x0eccbdff,0xffb999dd,0x4c3ff889,0xfa9fe1ff,
    0xfff83f63,0x32eeeeee,0x7ddddddd,0xff07ffc4,0x0017e45f,0x002fff98,
    0x37ffbbf6,0x8164c06f,0x70005fe8,0x27d403ff,0x37fffa60,0x7f541fc8,
    0x3aa01eff,0x22fb8dff,0xff90efeb,0x3ff403ff,0xffffea80,0xffffd885,
    0xffffb0ef,0x0bfb05df,0x53fc3ff2,0xff07ec7f,0x5fffffff,0x3bbbbba6,
    0x322ffc3e,0x00bf20ff,0x2006fe80,0x09fb06fb,0x001f4400,0x98807ea0,
    0x00011000,0x00088003,0x26002601,0x0088002c,0x4cc01310,0x00000009,
    0x00000000,0x00000000,0x4407ee00,0x3333264f,0x002ccccc,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x40000000,
    0x3fee0400,0x4fffffff,0x74400000,0x4039fb54,0x64400aa9,0xb9800001,
    0x000001bc,0x03372a00,0x4017bb93,0x16dc04c9,0x200e65c4,0x333100a8,
    0x81333333,0x4cc40bb9,0x09999999,0x26057700,0x257714c2,0x00000bb9,
    0x40000000,0xfeefcdf8,0x7fff444f,0x80be202f,0xc81d705c,0x40dfdcdf,
    0x3203645c,0xfd83320d,0x3f21ffff,0x6cc0ffff,0x3fe207ff,0x3fffea0f,
    0x41bf604f,0xfffffffa,0x0efb84ff,0x3ffffff2,0xfd802fff,0x11ff880d,
    0x54ffa3ff,0x000000ff,0x44000000,0x3fea3fff,0x3ee6ff60,0x8fc46a0f,
    0x717fa238,0x557641df,0x7ec5d88a,0x7d40ff23,0x1731be65,0x32049fb1,
    0x3f7fe23f,0x897ffc07,0x05fd10ff,0x5107fbf5,0x55555555,0x543be603,
    0xebbbbbbb,0x01fec02f,0xd1fe83fa,0x001fe67f,0x00000000,0x3e0ffe20,
    0xfc8bf31f,0x5cfd3fa3,0x57fa20ff,0x27e60efb,0x3f8afcfa,0x0fe83fa2,
    0x07fa0fe8,0x7ec0bf70,0x03fc45c2,0x1fd4bfea,0x75ba13ea,0x8800000f,
    0x05f90009,0x808004c4,0x3fccbf60,0x00000000,0x83fc4000,0xa87f52fd,
    0x77f7544f,0xefe880bf,0x6ab5c0ef,0xbf317299,0x8ff22fcc,0x7f4403fc,
    0x027ff542,0xff880ff1,0x4f987f70,0x44f98fdc,0x99999998,0x20000099,
    0x000002fc,0x37c4bf60,0x00000000,0x837c4000,0xb8bf32fd,0x1ffe883f,
    0x407ff440,0x21ddf54d,0x362fc86b,0x7ccdf12f,0x42ff4406,0x103ffdc9,
    0x25fc80ff,0x117e45f9,0x2a1fd8bf,0xffffffff,0x3200004f,0x0000002f,
    0x037c47f2,0x00000000,0xd837c400,0x223ff12f,0x37f220fe,0xf701dfdf,
    0x2ab90bff,0x88b90fae,0xd8df10ff,0x5407f62f,0x7f9804ff,0xd910ff10,
    0xbdfe81df,0x207e46fd,0x2eee66f8,0x02bbbbbb,0x00000000,0x00000000,
    0x00000000,0x17ec1be2,0x87ffdff7,0xfd33f2fe,0xfe8efb81,0xdb547d45,
    0x22fd89d4,0x137c42fd,0xbffb81df,0xff700999,0x3a61fe20,0xfffb102d,
    0xb827cc19,0x6d40003f,0x2ca8103d,0xffb80dcc,0x55534fff,0x00000555,
    0x00000000,0x7ec1be20,0x40e6e4c2,0x20c3f109,0xbfd10efb,0x99339dd8,
    0xf527dc1f,0xfb93ee0b,0x3ffffe25,0xffddb2ff,0xffffe85f,0x010001ff,
    0x00130062,0x2ffffdc0,0x7ffccbee,0x503fff11,0x3a799999,0x007fffff,
    0x00000000,0x0df10000,0x10000bf6,0x2077405f,0x3ba20fe8,0x741fda9b,
    0xfd007f46,0x999076c1,0x3ae39999,0xccb80bde,0x0000cccc,0x80000000,
    0x8dfd89fe,0xfff50fd8,0x00fffe65,0x333332e0,0x00000004,0x10000000,
    0x4cbf60df,0x3eeeeeee,0x04401510,0xdfb70088,0x00202019,0x00000101,
    0x00000000,0x7c400000,0x2ffffec5,0x1ffd1bfa,0x00000000,0x00000000,
    0x20df1000,0xdddd32fd,0x00007ddd,0x00000000,0x00000000,0x00000000,
    0x06a00000,0x80413bae,0x00000009,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};

static signed short stb__consolas_24_latin1_x[224]={ 0,5,3,0,1,0,0,5,3,3,1,0,2,3,
4,1,1,1,1,1,0,2,1,1,1,1,4,2,1,1,2,3,0,0,1,1,1,2,2,0,1,2,2,1,
2,0,1,0,1,0,1,1,1,1,0,0,0,0,1,4,1,3,1,0,0,1,1,1,1,1,0,1,1,2,
1,2,2,1,1,1,1,1,2,2,0,1,0,0,0,0,1,1,5,2,0,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,5,1,1,1,0,
5,1,0,0,2,1,1,3,1,0,2,1,2,3,0,1,1,4,5,2,2,1,0,0,0,2,0,0,0,0,
0,0,-1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,
0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,
 };
static signed short stb__consolas_24_latin1_y[224]={ 17,0,0,1,-1,0,0,0,-1,-1,0,4,13,9,
13,0,1,1,1,1,1,1,1,1,1,1,5,5,4,7,4,0,0,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,20,0,5,0,5,0,5,0,5,0,0,
0,0,0,5,5,5,5,5,5,5,1,5,5,5,5,5,5,0,-3,0,8,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,17,5,-1,1,2,1,
-3,0,0,1,1,5,9,9,0,1,0,2,0,0,0,5,0,8,17,0,1,5,0,0,0,5,-3,-3,-3,-3,
-3,-4,1,1,-3,-3,-3,-3,-3,-3,-3,-3,1,-3,-3,-3,-3,-3,-3,5,-1,-3,-3,-3,-3,-3,1,0,0,0,
0,0,0,-1,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,2,0,0,0,0,0,0,0,
 };
static unsigned short stb__consolas_24_latin1_w[224]={ 0,4,8,13,11,13,14,3,8,7,11,13,7,8,
5,11,12,11,11,11,13,10,11,11,11,11,5,7,10,11,10,8,14,14,11,11,12,10,10,12,11,10,9,12,
10,13,11,13,11,14,12,11,12,11,14,13,13,14,11,6,11,7,11,14,8,11,11,11,11,11,13,12,11,10,
10,11,10,12,11,12,11,11,11,10,12,11,13,13,13,13,11,10,3,10,13,12,12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,0,4,10,11,12,13,
3,11,11,14,9,11,11,8,11,10,9,11,9,8,12,12,11,5,3,9,9,11,13,13,13,8,14,14,14,14,
14,14,14,11,12,12,12,12,12,12,12,12,13,12,13,13,13,13,13,11,13,12,12,12,12,14,11,11,12,12,
12,12,12,12,13,11,12,12,12,12,12,12,12,12,11,12,13,13,13,13,13,13,12,12,12,12,12,13,11,13,
 };
static unsigned short stb__consolas_24_latin1_h[224]={ 0,18,6,16,21,18,18,6,23,23,11,13,9,3,
5,20,17,16,16,17,16,17,17,16,17,16,13,17,14,7,14,18,22,16,16,17,16,16,16,17,16,16,17,16,
16,16,16,17,16,21,16,17,16,17,16,16,16,16,16,22,20,22,9,2,6,13,18,13,18,13,17,17,17,17,
22,17,17,12,12,13,17,17,12,13,17,13,12,12,12,17,12,22,25,22,5,16,16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,0,17,21,16,15,16,
25,20,6,17,12,11,6,3,11,5,9,15,10,10,6,17,20,5,4,10,12,11,17,17,17,17,20,20,20,20,
20,21,16,20,20,20,20,20,20,20,20,20,16,20,21,21,21,21,21,11,21,21,21,21,21,20,16,18,18,18,
18,18,18,19,13,16,18,18,18,18,17,17,17,17,18,17,18,18,18,18,18,13,18,18,18,18,18,22,22,22,
 };
static unsigned short stb__consolas_24_latin1_s[224]={ 252,250,247,62,40,106,104,252,17,9,70,
48,159,238,215,91,183,221,233,12,36,1,222,13,152,220,247,223,37,189,26,
40,100,232,1,24,194,183,25,36,50,76,49,87,245,112,196,1,100,129,207,
164,208,176,181,167,153,138,126,34,146,26,177,26,201,62,119,127,171,139,65,
15,40,245,89,74,141,176,48,74,79,28,225,151,52,87,237,211,162,231,189,
41,1,64,201,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,
170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,252,247,157,
143,1,103,5,186,235,59,201,118,210,238,94,227,167,14,130,140,222,196,79,
221,252,149,60,106,86,113,127,201,198,213,66,118,103,52,155,67,133,173,14,
27,241,1,40,53,129,228,168,182,196,210,224,82,238,1,14,27,144,158,117,
94,172,185,198,211,131,81,99,91,133,159,146,120,234,209,210,188,224,100,236,
49,157,90,63,113,144,27,1,77,14,75,52,115, };
static unsigned short stb__consolas_24_latin1_t[224]={ 13,49,156,125,27,49,70,1,1,1,156,
142,156,163,163,27,70,125,125,89,125,89,70,125,89,107,107,89,142,156,142,
70,1,107,125,89,107,107,125,89,125,125,89,125,125,125,125,107,125,1,107,
89,125,89,125,125,125,125,125,1,27,1,156,24,156,142,70,142,70,142,107,
107,107,89,1,89,89,142,156,142,107,107,142,142,107,142,142,142,142,89,142,
1,1,1,163,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,
107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,13,70,1,
107,142,107,1,27,156,89,142,156,156,163,156,163,156,142,156,156,156,70,27,
163,8,156,156,156,89,89,89,89,27,27,49,27,27,27,107,27,27,27,49,
49,27,49,49,49,107,27,1,1,1,1,1,156,1,27,27,27,1,27,107,
49,49,49,49,49,70,49,142,107,49,49,49,49,70,70,89,89,49,89,49,
70,70,70,70,142,70,70,70,70,70,1,1,1, };
static unsigned short stb__consolas_24_latin1_a[224]={ 211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,211,
211,211,211,211,211,211,211,211, };

// Call this function with
//    font: NULL or array length
//    data: NULL or specified size
//    height: STB_FONT_consolas_24_latin1_BITMAP_HEIGHT or STB_FONT_consolas_24_latin1_BITMAP_HEIGHT_POW2
//    return value: spacing between lines
static void stb_font_consolas_24_latin1(stb_fontchar font[STB_FONT_consolas_24_latin1_NUM_CHARS],
                unsigned char data[STB_FONT_consolas_24_latin1_BITMAP_HEIGHT][STB_FONT_consolas_24_latin1_BITMAP_WIDTH],
                int height)
{
    int i,j;
    if (data != 0) {
        unsigned int *bits = stb__consolas_24_latin1_pixels;
        unsigned int bitpack = *bits++, numbits = 32;
        for (i=0; i < STB_FONT_consolas_24_latin1_BITMAP_WIDTH*height; ++i)
            data[0][i] = 0;  // zero entire bitmap
        for (j=1; j < STB_FONT_consolas_24_latin1_BITMAP_HEIGHT-1; ++j) {
            for (i=1; i < STB_FONT_consolas_24_latin1_BITMAP_WIDTH-1; ++i) {
                unsigned int value;
                if (numbits==0) bitpack = *bits++, numbits=32;
                value = bitpack & 1;
                bitpack >>= 1, --numbits;
                if (value) {
                    if (numbits < 3) bitpack = *bits++, numbits = 32;
                    data[j][i] = (bitpack & 7) * 0x20 + 0x1f;
                    bitpack >>= 3, numbits -= 3;
                } else {
                    data[j][i] = 0;
                }
            }
        }
    }

    // build font description
    if (font != 0) {
        float recip_width = 1.0f / STB_FONT_consolas_24_latin1_BITMAP_WIDTH;
        float recip_height = 1.0f / height;
        for (i=0; i < STB_FONT_consolas_24_latin1_NUM_CHARS; ++i) {
            // pad characters so they bilerp from empty space around each character
            font[i].s0 = (stb__consolas_24_latin1_s[i]) * recip_width;
            font[i].t0 = (stb__consolas_24_latin1_t[i]) * recip_height;
            font[i].s1 = (stb__consolas_24_latin1_s[i] + stb__consolas_24_latin1_w[i]) * recip_width;
            font[i].t1 = (stb__consolas_24_latin1_t[i] + stb__consolas_24_latin1_h[i]) * recip_height;
            font[i].x0 = stb__consolas_24_latin1_x[i];
            font[i].y0 = stb__consolas_24_latin1_y[i];
            font[i].x1 = stb__consolas_24_latin1_x[i] + stb__consolas_24_latin1_w[i];
            font[i].y1 = stb__consolas_24_latin1_y[i] + stb__consolas_24_latin1_h[i];
            font[i].advance_int = (stb__consolas_24_latin1_a[i]+8)>>4;
            font[i].s0f = (stb__consolas_24_latin1_s[i] - 0.5f) * recip_width;
            font[i].t0f = (stb__consolas_24_latin1_t[i] - 0.5f) * recip_height;
            font[i].s1f = (stb__consolas_24_latin1_s[i] + stb__consolas_24_latin1_w[i] + 0.5f) * recip_width;
            font[i].t1f = (stb__consolas_24_latin1_t[i] + stb__consolas_24_latin1_h[i] + 0.5f) * recip_height;
            font[i].x0f = stb__consolas_24_latin1_x[i] - 0.5f;
            font[i].y0f = stb__consolas_24_latin1_y[i] - 0.5f;
            font[i].x1f = stb__consolas_24_latin1_x[i] + stb__consolas_24_latin1_w[i] + 0.5f;
            font[i].y1f = stb__consolas_24_latin1_y[i] + stb__consolas_24_latin1_h[i] + 0.5f;
            font[i].advance = stb__consolas_24_latin1_a[i]/16.0f;
        }
    }
}

#ifndef STB_SOMEFONT_CREATE
#define STB_SOMEFONT_CREATE              stb_font_consolas_24_latin1
#define STB_SOMEFONT_BITMAP_WIDTH        STB_FONT_consolas_24_latin1_BITMAP_WIDTH
#define STB_SOMEFONT_BITMAP_HEIGHT       STB_FONT_consolas_24_latin1_BITMAP_HEIGHT
#define STB_SOMEFONT_BITMAP_HEIGHT_POW2  STB_FONT_consolas_24_latin1_BITMAP_HEIGHT_POW2
#define STB_SOMEFONT_FIRST_CHAR          STB_FONT_consolas_24_latin1_FIRST_CHAR
#define STB_SOMEFONT_NUM_CHARS           STB_FONT_consolas_24_latin1_NUM_CHARS
#define STB_SOMEFONT_LINE_SPACING        STB_FONT_consolas_24_latin1_LINE_SPACING
#endif


A src/VulkanInitializers.hpp => src/VulkanInitializers.hpp +542 -0
@@ 0,0 1,542 @@
/*
* Initializers for Vulkan structures and objects used by the examples
* Saves lot of VK_STRUCTURE_TYPE assignments
* Some initializers are parameterized for convenience
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/

#pragma once

#include "vulkan/vulkan.h"

namespace vkTools
{
	namespace initializers
	{

		inline VkMemoryAllocateInfo memoryAllocateInfo()
		{
			VkMemoryAllocateInfo memAllocInfo {};
			memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
			return memAllocInfo;
		}

		inline VkMappedMemoryRange mappedMemoryRange()
		{
			VkMappedMemoryRange mappedMemoryRange {};
			mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
			return mappedMemoryRange;
		}

		inline VkCommandBufferAllocateInfo commandBufferAllocateInfo(
			VkCommandPool commandPool, 
			VkCommandBufferLevel level, 
			uint32_t bufferCount)
		{
			VkCommandBufferAllocateInfo commandBufferAllocateInfo {};
			commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
			commandBufferAllocateInfo.commandPool = commandPool;
			commandBufferAllocateInfo.level = level;
			commandBufferAllocateInfo.commandBufferCount = bufferCount;
			return commandBufferAllocateInfo;
		}

		inline VkCommandPoolCreateInfo commandPoolCreateInfo()
		{
			VkCommandPoolCreateInfo cmdPoolCreateInfo {};
			cmdPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
			return cmdPoolCreateInfo;
		}

		inline VkCommandBufferBeginInfo commandBufferBeginInfo()
		{
			VkCommandBufferBeginInfo cmdBufferBeginInfo {};
			cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
			return cmdBufferBeginInfo;
		}

		inline VkCommandBufferInheritanceInfo commandBufferInheritanceInfo()
		{
			VkCommandBufferInheritanceInfo cmdBufferInheritanceInfo {};
			cmdBufferInheritanceInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
			return cmdBufferInheritanceInfo;
		}

		inline VkRenderPassBeginInfo renderPassBeginInfo()
		{
			VkRenderPassBeginInfo renderPassBeginInfo {};
			renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
			return renderPassBeginInfo;
		}

		inline VkRenderPassCreateInfo renderPassCreateInfo()
		{
			VkRenderPassCreateInfo renderPassCreateInfo {};
			renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
			return renderPassCreateInfo;
		}

		/** @brief Initialize an image memory barrier with no image transfer ownership */
		inline VkImageMemoryBarrier imageMemoryBarrier()
		{
			VkImageMemoryBarrier imageMemoryBarrier {};
			imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
			imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
			imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
			return imageMemoryBarrier;
		}

		/** @brief Initialize a buffer memory barrier with no image transfer ownership */
		inline VkBufferMemoryBarrier bufferMemoryBarrier()
		{
			VkBufferMemoryBarrier bufferMemoryBarrier {};
			bufferMemoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
			bufferMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
			bufferMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
			return bufferMemoryBarrier;
		}

		inline VkMemoryBarrier memoryBarrier()
		{
			VkMemoryBarrier memoryBarrier {};
			memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
			return memoryBarrier;
		}

		inline VkImageCreateInfo imageCreateInfo()
		{
			VkImageCreateInfo imageCreateInfo {};
			imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
			return imageCreateInfo;
		}

		inline VkSamplerCreateInfo samplerCreateInfo()
		{
			VkSamplerCreateInfo samplerCreateInfo {};
			samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
			return samplerCreateInfo;
		}

		inline VkImageViewCreateInfo imageViewCreateInfo()
		{
			VkImageViewCreateInfo imageViewCreateInfo {};
			imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
			return imageViewCreateInfo;
		}

		inline VkFramebufferCreateInfo framebufferCreateInfo()
		{
			VkFramebufferCreateInfo framebufferCreateInfo {};
			framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
			return framebufferCreateInfo;
		}

		inline VkSemaphoreCreateInfo semaphoreCreateInfo()
		{
			VkSemaphoreCreateInfo semaphoreCreateInfo {};
			semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
			return semaphoreCreateInfo;
		}

		inline VkFenceCreateInfo fenceCreateInfo(VkFenceCreateFlags flags = 0)
		{
			VkFenceCreateInfo fenceCreateInfo {};
			fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
			fenceCreateInfo.flags = flags;
			return fenceCreateInfo;
		}

		inline VkEventCreateInfo eventCreateInfo()
		{
			VkEventCreateInfo eventCreateInfo {};
			eventCreateInfo.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
			return eventCreateInfo;
		}

		inline VkSubmitInfo submitInfo()
		{
			VkSubmitInfo submitInfo {};
			submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
			return submitInfo;
		}

		inline VkViewport viewport(
			float width,
			float height,
			float minDepth,
			float maxDepth)
		{
			VkViewport viewport {};
			viewport.width = width;
			viewport.height = height;
			viewport.minDepth = minDepth;
			viewport.maxDepth = maxDepth;
			return viewport;
		}

		inline VkRect2D rect2D(
			int32_t width,
			int32_t height,
			int32_t offsetX,
			int32_t offsetY)
		{
			VkRect2D rect2D {};
			rect2D.extent.width = width;
			rect2D.extent.height = height;
			rect2D.offset.x = offsetX;
			rect2D.offset.y = offsetY;
			return rect2D;
		}

		inline VkBufferCreateInfo bufferCreateInfo()
		{
			VkBufferCreateInfo bufCreateInfo {};
			bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
			return bufCreateInfo;
		}

		inline VkBufferCreateInfo bufferCreateInfo(
			VkBufferUsageFlags usage,
			VkDeviceSize size)
		{
			VkBufferCreateInfo bufCreateInfo {};
			bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
			bufCreateInfo.usage = usage;
			bufCreateInfo.size = size;
			return bufCreateInfo;
		}

		inline VkDescriptorPoolCreateInfo descriptorPoolCreateInfo(
			uint32_t poolSizeCount,
			VkDescriptorPoolSize* pPoolSizes,
			uint32_t maxSets)
		{
			VkDescriptorPoolCreateInfo descriptorPoolInfo {};
			descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
			descriptorPoolInfo.poolSizeCount = poolSizeCount;
			descriptorPoolInfo.pPoolSizes = pPoolSizes;
			descriptorPoolInfo.maxSets = maxSets;
			return descriptorPoolInfo;
		}

		inline VkDescriptorPoolSize descriptorPoolSize(
			VkDescriptorType type,
			uint32_t descriptorCount)
		{
			VkDescriptorPoolSize descriptorPoolSize {};
			descriptorPoolSize.type = type;
			descriptorPoolSize.descriptorCount = descriptorCount;
			return descriptorPoolSize;
		}

		inline VkDescriptorSetLayoutBinding descriptorSetLayoutBinding(
			VkDescriptorType type,
			VkShaderStageFlags stageFlags,
			uint32_t binding,
			uint32_t descriptorCount = 1)
		{
			VkDescriptorSetLayoutBinding setLayoutBinding {};
			setLayoutBinding.descriptorType = type;
			setLayoutBinding.stageFlags = stageFlags;
			setLayoutBinding.binding = binding;
			setLayoutBinding.descriptorCount = descriptorCount;
			return setLayoutBinding;
		}

		inline VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo(
			const VkDescriptorSetLayoutBinding* pBindings,
			uint32_t bindingCount)
		{
			VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo {};
			descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
			descriptorSetLayoutCreateInfo.pBindings = pBindings;
			descriptorSetLayoutCreateInfo.bindingCount = bindingCount;
			return descriptorSetLayoutCreateInfo;
		}

		inline VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo(
			const VkDescriptorSetLayout* pSetLayouts,
			uint32_t setLayoutCount = 1)
		{
			VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo {};
			pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
			pipelineLayoutCreateInfo.setLayoutCount = setLayoutCount;
			pipelineLayoutCreateInfo.pSetLayouts = pSetLayouts;
			return pipelineLayoutCreateInfo;
		}

		inline VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo(
			uint32_t setLayoutCount = 1)
		{
			VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{};
			pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
			pipelineLayoutCreateInfo.setLayoutCount = setLayoutCount;
			return pipelineLayoutCreateInfo;
		}

		inline VkDescriptorSetAllocateInfo descriptorSetAllocateInfo(
			VkDescriptorPool descriptorPool,
			const VkDescriptorSetLayout* pSetLayouts,
			uint32_t descriptorSetCount)
		{
			VkDescriptorSetAllocateInfo descriptorSetAllocateInfo {};
			descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
			descriptorSetAllocateInfo.descriptorPool = descriptorPool;
			descriptorSetAllocateInfo.pSetLayouts = pSetLayouts;
			descriptorSetAllocateInfo.descriptorSetCount = descriptorSetCount;
			return descriptorSetAllocateInfo;
		}

		inline VkDescriptorImageInfo descriptorImageInfo(VkSampler sampler, VkImageView imageView, VkImageLayout imageLayout)
		{
			VkDescriptorImageInfo descriptorImageInfo {};
			descriptorImageInfo.sampler = sampler;
			descriptorImageInfo.imageView = imageView;
			descriptorImageInfo.imageLayout = imageLayout;
			return descriptorImageInfo;
		}

		inline VkWriteDescriptorSet writeDescriptorSet(
			VkDescriptorSet dstSet,
			VkDescriptorType type,
			uint32_t binding,
			VkDescriptorBufferInfo* bufferInfo,
			uint32_t descriptorCount = 1)
		{
			VkWriteDescriptorSet writeDescriptorSet {};
			writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
			writeDescriptorSet.dstSet = dstSet;
			writeDescriptorSet.descriptorType = type;
			writeDescriptorSet.dstBinding = binding;
			writeDescriptorSet.pBufferInfo = bufferInfo;
			writeDescriptorSet.descriptorCount = descriptorCount;
			return writeDescriptorSet;
		}

		inline VkWriteDescriptorSet writeDescriptorSet(
			VkDescriptorSet dstSet,
			VkDescriptorType type,
			uint32_t binding,
			VkDescriptorImageInfo *imageInfo,
			uint32_t descriptorCount = 1)
		{
			VkWriteDescriptorSet writeDescriptorSet {};
			writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
			writeDescriptorSet.dstSet = dstSet;
			writeDescriptorSet.descriptorType = type;
			writeDescriptorSet.dstBinding = binding;
			writeDescriptorSet.pImageInfo = imageInfo;
			writeDescriptorSet.descriptorCount = descriptorCount;
			return writeDescriptorSet;
		}

		inline VkVertexInputBindingDescription vertexInputBindingDescription(
			uint32_t binding,
			uint32_t stride,
			VkVertexInputRate inputRate)
		{
			VkVertexInputBindingDescription vInputBindDescription {};
			vInputBindDescription.binding = binding;
			vInputBindDescription.stride = stride;
			vInputBindDescription.inputRate = inputRate;
			return vInputBindDescription;
		}

		inline VkVertexInputAttributeDescription vertexInputAttributeDescription(
			uint32_t binding,
			uint32_t location,
			VkFormat format,
			uint32_t offset)
		{
			VkVertexInputAttributeDescription vInputAttribDescription {};
			vInputAttribDescription.location = location;
			vInputAttribDescription.binding = binding;
			vInputAttribDescription.format = format;
			vInputAttribDescription.offset = offset;
			return vInputAttribDescription;
		}

		inline VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo()
		{
			VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo {};
			pipelineVertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
			return pipelineVertexInputStateCreateInfo;
		}

		inline VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(
			VkPrimitiveTopology topology,
			VkPipelineInputAssemblyStateCreateFlags flags,
			VkBool32 primitiveRestartEnable)
		{
			VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo {};
			pipelineInputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
			pipelineInputAssemblyStateCreateInfo.topology = topology;
			pipelineInputAssemblyStateCreateInfo.flags = flags;
			pipelineInputAssemblyStateCreateInfo.primitiveRestartEnable = primitiveRestartEnable;
			return pipelineInputAssemblyStateCreateInfo;
		}

		inline VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo(
			VkPolygonMode polygonMode,
			VkCullModeFlags cullMode,
			VkFrontFace frontFace,
			VkPipelineRasterizationStateCreateFlags flags)
		{
			VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo {};
			pipelineRasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
			pipelineRasterizationStateCreateInfo.polygonMode = polygonMode;
			pipelineRasterizationStateCreateInfo.cullMode = cullMode;
			pipelineRasterizationStateCreateInfo.frontFace = frontFace;
			pipelineRasterizationStateCreateInfo.flags = flags;
			pipelineRasterizationStateCreateInfo.depthClampEnable = VK_FALSE;
			pipelineRasterizationStateCreateInfo.lineWidth = 1.0f;
			return pipelineRasterizationStateCreateInfo;
		}

		inline VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState(
			VkColorComponentFlags colorWriteMask,
			VkBool32 blendEnable)
		{
			VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState {};
			pipelineColorBlendAttachmentState.colorWriteMask = colorWriteMask;
			pipelineColorBlendAttachmentState.blendEnable = blendEnable;
			return pipelineColorBlendAttachmentState;
		}

		inline VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo(
			uint32_t attachmentCount,
			const VkPipelineColorBlendAttachmentState * pAttachments)
		{
			VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo {};
			pipelineColorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
			pipelineColorBlendStateCreateInfo.attachmentCount = attachmentCount;
			pipelineColorBlendStateCreateInfo.pAttachments = pAttachments;
			return pipelineColorBlendStateCreateInfo;
		}

		inline VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo(
			VkBool32 depthTestEnable,
			VkBool32 depthWriteEnable,
			VkCompareOp depthCompareOp)
		{
			VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo {};
			pipelineDepthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
			pipelineDepthStencilStateCreateInfo.depthTestEnable = depthTestEnable;
			pipelineDepthStencilStateCreateInfo.depthWriteEnable = depthWriteEnable;
			pipelineDepthStencilStateCreateInfo.depthCompareOp = depthCompareOp;
			pipelineDepthStencilStateCreateInfo.front = pipelineDepthStencilStateCreateInfo.back;
			pipelineDepthStencilStateCreateInfo.back.compareOp = VK_COMPARE_OP_ALWAYS;
			return pipelineDepthStencilStateCreateInfo;
		}

		inline VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(
			uint32_t viewportCount,
			uint32_t scissorCount,
			VkPipelineViewportStateCreateFlags flags)
		{
			VkPipelineViewportStateCreateInfo pipelineViewportStateCreateInfo {};
			pipelineViewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
			pipelineViewportStateCreateInfo.viewportCount = viewportCount;
			pipelineViewportStateCreateInfo.scissorCount = scissorCount;
			pipelineViewportStateCreateInfo.flags = flags;
			return pipelineViewportStateCreateInfo;
		}

		inline VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo(
			VkSampleCountFlagBits rasterizationSamples,
			VkPipelineMultisampleStateCreateFlags flags)
		{
			VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo {};
			pipelineMultisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
			pipelineMultisampleStateCreateInfo.rasterizationSamples = rasterizationSamples;
			return pipelineMultisampleStateCreateInfo;
		}

		inline VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(
			const VkDynamicState * pDynamicStates,
			uint32_t dynamicStateCount,
			VkPipelineDynamicStateCreateFlags flags = 0)
		{
			VkPipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo {};
			pipelineDynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
			pipelineDynamicStateCreateInfo.pDynamicStates = pDynamicStates;
			pipelineDynamicStateCreateInfo.dynamicStateCount = dynamicStateCount;
			return pipelineDynamicStateCreateInfo;
		}

		inline VkPipelineTessellationStateCreateInfo pipelineTessellationStateCreateInfo(uint32_t patchControlPoints)
		{
			VkPipelineTessellationStateCreateInfo pipelineTessellationStateCreateInfo {};
			pipelineTessellationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
			pipelineTessellationStateCreateInfo.patchControlPoints = patchControlPoints;
			return pipelineTessellationStateCreateInfo;
		}

		inline VkGraphicsPipelineCreateInfo pipelineCreateInfo(
			VkPipelineLayout layout,
			VkRenderPass renderPass,
			VkPipelineCreateFlags flags = 0)
		{
			VkGraphicsPipelineCreateInfo pipelineCreateInfo {};
			pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
			pipelineCreateInfo.layout = layout;
			pipelineCreateInfo.renderPass = renderPass;
			pipelineCreateInfo.flags = flags;
			return pipelineCreateInfo;
		}

		inline VkComputePipelineCreateInfo computePipelineCreateInfo(
			VkPipelineLayout layout, 
			VkPipelineCreateFlags flags = 0)
		{
			VkComputePipelineCreateInfo computePipelineCreateInfo {};
			computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
			computePipelineCreateInfo.layout = layout;
			computePipelineCreateInfo.flags = flags;
			return computePipelineCreateInfo;
		}

		inline VkPushConstantRange pushConstantRange(
			VkShaderStageFlags stageFlags,
			uint32_t size,
			uint32_t offset)
		{
			VkPushConstantRange pushConstantRange {};
			pushConstantRange.stageFlags = stageFlags;
			pushConstantRange.offset = offset;
			pushConstantRange.size = size;
			return pushConstantRange;
		}

		inline VkBindSparseInfo bindSparseInfo()
		{
			VkBindSparseInfo bindSparseInfo{};
			bindSparseInfo.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO;
			return bindSparseInfo;
		}

		/** @brief Initialize a map entry for a shader specialization constant */
		inline VkSpecializationMapEntry specializationMapEntry(uint32_t constantID, uint32_t offset, size_t size)
		{
			VkSpecializationMapEntry specializationMapEntry{};
			specializationMapEntry.constantID = constantID;
			specializationMapEntry.offset = offset;
			specializationMapEntry.size = size;
			return specializationMapEntry;
		}

		/** @brief Initialize a specialization constant info structure to pass to a shader stage */
		inline VkSpecializationInfo specializationInfo(uint32_t mapEntryCount, const VkSpecializationMapEntry* mapEntries, size_t dataSize, const void* data)
		{
			VkSpecializationInfo specializationInfo{};
			specializationInfo.mapEntryCount = mapEntryCount;
			specializationInfo.pMapEntries = mapEntries;
			specializationInfo.dataSize = dataSize;
			specializationInfo.pData = data;
			return specializationInfo;
		}
	}
}
\ No newline at end of file

A src/camera.hpp => src/camera.hpp +205 -0
@@ 0,0 1,205 @@
/*
* Basic camera class
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/

#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>

class Camera
{
private:
	float fov;
	float znear, zfar;

	void updateViewMatrix()
	{
		glm::mat4 rotM = glm::mat4();
		glm::mat4 transM;

		rotM = glm::rotate(rotM, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
		rotM = glm::rotate(rotM, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
		rotM = glm::rotate(rotM, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));

		transM = glm::translate(glm::mat4(), position);

		if (type == CameraType::firstperson)
		{
			matrices.view = rotM * transM;
		}
		else
		{
			matrices.view = transM * rotM;
		}
	};
public:
	enum CameraType { lookat, firstperson };
	CameraType type = CameraType::lookat;

	glm::vec3 rotation = glm::vec3();
	glm::vec3 position = glm::vec3();

	float rotationSpeed = 1.0f;
	float movementSpeed = 1.0f;

	struct
	{
		glm::mat4 perspective;
		glm::mat4 view;
	} matrices;

	struct
	{
		bool left = false;
		bool right = false;
		bool up = false;
		bool down = false;
	} keys;

	bool moving()
	{
		return keys.left || keys.right || keys.up || keys.down;
	}

	void setPerspective(float fov, float aspect, float znear, float zfar)
	{
		this->fov = fov;
		this->znear = znear;
		this->zfar = zfar;
		matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar);
	};

	void updateAspectRatio(float aspect)
	{
		matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar);
	}

	void setPosition(glm::vec3 position)
	{
		this->position = position;
		updateViewMatrix();
	}

	void setRotation(glm::vec3 rotation)
	{
		this->rotation = rotation;
		updateViewMatrix();
	};

	void rotate(glm::vec3 delta)
	{
		this->rotation += delta;
		updateViewMatrix();
	}

	void setTranslation(glm::vec3 translation)
	{
		this->position = translation;
		updateViewMatrix();
	};

	void translate(glm::vec3 delta)
	{
		this->position += delta;
		updateViewMatrix();
	}

	void update(float deltaTime)
	{
		if (type == CameraType::firstperson)
		{
			if (moving())
			{
				glm::vec3 camFront;
				camFront.x = -cos(glm::radians(rotation.x)) * sin(glm::radians(rotation.y));
				camFront.y = sin(glm::radians(rotation.x));
				camFront.z = cos(glm::radians(rotation.x)) * cos(glm::radians(rotation.y));
				camFront = glm::normalize(camFront);

				float moveSpeed = deltaTime * movementSpeed;

				if (keys.up)
					position += camFront * moveSpeed;
				if (keys.down)
					position -= camFront * moveSpeed;
				if (keys.left)
					position -= glm::normalize(glm::cross(camFront, glm::vec3(0.0f, 1.0f, 0.0f))) * moveSpeed;
				if (keys.right)
					position += glm::normalize(glm::cross(camFront, glm::vec3(0.0f, 1.0f, 0.0f))) * moveSpeed;

				updateViewMatrix();
			}
		}
	};

	// Update camera passing separate axis data (gamepad)
	// Returns true if view or position has been changed
	bool updatePad(glm::vec2 axisLeft, glm::vec2 axisRight, float deltaTime)
	{
		bool retVal = false;

		if (type == CameraType::firstperson)
		{
			// Use the common console thumbstick layout		
			// Left = view, right = move

			const float deadZone = 0.0015f;
			const float range = 1.0f - deadZone;

			glm::vec3 camFront;
			camFront.x = -cos(glm::radians(rotation.x)) * sin(glm::radians(rotation.y));
			camFront.y = sin(glm::radians(rotation.x));
			camFront.z = cos(glm::radians(rotation.x)) * cos(glm::radians(rotation.y));
			camFront = glm::normalize(camFront);

			float moveSpeed = deltaTime * movementSpeed * 2.0f;
			float rotSpeed = deltaTime * rotationSpeed * 50.0f;
			 
			// Move
			if (fabsf(axisLeft.y) > deadZone)
			{
				float pos = (fabsf(axisLeft.y) - deadZone) / range;
				position -= camFront * pos * ((axisLeft.y < 0.0f) ? -1.0f : 1.0f) * moveSpeed;
				retVal = true;
			}
			if (fabsf(axisLeft.x) > deadZone)
			{
				float pos = (fabsf(axisLeft.x) - deadZone) / range;
				position += glm::normalize(glm::cross(camFront, glm::vec3(0.0f, 1.0f, 0.0f))) * pos * ((axisLeft.x < 0.0f) ? -1.0f : 1.0f) * moveSpeed;
				retVal = true;
			}

			// Rotate
			if (fabsf(axisRight.x) > deadZone)
			{
				float pos = (fabsf(axisRight.x) - deadZone) / range;
				rotation.y += pos * ((axisRight.x < 0.0f) ? -1.0f : 1.0f) * rotSpeed;
				retVal = true;
			}
			if (fabsf(axisRight.y) > deadZone)
			{
				float pos = (fabsf(axisRight.y) - deadZone) / range;
				rotation.x -= pos * ((axisRight.y < 0.0f) ? -1.0f : 1.0f) * rotSpeed;
				retVal = true;
			}
		}
		else
		{
			// todo: move code from example base class for look-at
		}

		if (retVal)
		{
			updateViewMatrix();
		}

		return retVal;
	}

};
\ No newline at end of file

A src/keycodes.hpp => src/keycodes.hpp +90 -0
@@ 0,0 1,90 @@
/*
* Key codes for multiple platforms
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/

#pragma once

#if defined(_WIN32)
#define KEY_ESCAPE VK_ESCAPE 
#define KEY_F1 VK_F1
#define KEY_F2 VK_F2
#define KEY_F3 VK_F3
#define KEY_F4 VK_F4
#define KEY_W 0x57
#define KEY_A 0x41
#define KEY_S 0x53
#define KEY_D 0x44
#define KEY_P 0x50
#define KEY_SPACE 0x20
#define KEY_KPADD 0x6B
#define KEY_KPSUB 0x6D
#define KEY_B 0x42
#define KEY_F 0x46
#define KEY_L 0x4C
#define KEY_N 0x4E
#define KEY_O 0x4F
#define KEY_T 0x54
#elif defined(__ANDROID__)
// Dummy key codes 
#define KEY_ESCAPE 0x0
#define KEY_F1 0x1
#define KEY_F2 0x2
#define KEY_F2 0x11
#define KEY_F2 0x12
#define KEY_F3 0x13
#define KEY_F4 0x14
#define KEY_W 0x3
#define KEY_A 0x4
#define KEY_S 0x5
#define KEY_D 0x6
#define KEY_P 0x7
#define KEY_SPACE 0x8
#define KEY_KPADD 0x9
#define KEY_KPSUB 0xA
#define KEY_B 0xB
#define KEY_F 0xC
#define KEY_L 0xD
#define KEY_N 0xE
#define KEY_O 0xF
#define KEY_T 0x10
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
#include <linux/input.h>

// todo: hack for bloom example
#define KEY_KPADD KEY_KPPLUS
#define KEY_KPSUB KEY_KPMINUS

#elif defined(__linux__)
#define KEY_ESCAPE 0x9
#define KEY_F1 0x43
#define KEY_F2 0x44
#define KEY_F3 0x45
#define KEY_F4 0x46
#define KEY_W 0x19
#define KEY_A 0x26
#define KEY_S 0x27
#define KEY_D 0x28
#define KEY_P 0x21
#define KEY_SPACE 0x41
#define KEY_KPADD 0x56
#define KEY_KPSUB 0x52
#define KEY_B 0x38
#define KEY_F 0x29
#define KEY_L 0x2E
#define KEY_N 0x39
#define KEY_O 0x20
#define KEY_T 0x1C
#endif

// todo: Android gamepad keycodes outside of define for now
#define GAMEPAD_BUTTON_A 0x1000
#define GAMEPAD_BUTTON_B 0x1001
#define GAMEPAD_BUTTON_X 0x1002
#define GAMEPAD_BUTTON_Y 0x1003
#define GAMEPAD_BUTTON_L1 0x1004
#define GAMEPAD_BUTTON_R1 0x1005
#define GAMEPAD_BUTTON_START 0x1006

A src/vulkanMeshLoader.hpp => src/vulkanMeshLoader.hpp +648 -0
@@ 0,0 1,648 @@
/*
* Mesh loader for creating Vulkan resources from models loaded with ASSIMP
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/

#pragma once

#include <stdlib.h>
#include <string>
#include <fstream>
#include <assert.h>
#include <stdio.h>
#include <vector>
#include <map>
#ifdef _WIN32
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#else
#endif

#include "vulkan/vulkan.h"

#include <assimp/Importer.hpp> 
#include <assimp/scene.h>     
#include <assimp/postprocess.h>
#include <assimp/cimport.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "vulkandevice.hpp"

#if defined(__ANDROID__)
#include <android/asset_manager.h>
#endif

namespace vkMeshLoader 
{
	typedef enum VertexLayout {
		VERTEX_LAYOUT_POSITION = 0x0,
		VERTEX_LAYOUT_NORMAL = 0x1,
		VERTEX_LAYOUT_COLOR = 0x2,
		VERTEX_LAYOUT_UV = 0x3,
		VERTEX_LAYOUT_TANGENT = 0x4,
		VERTEX_LAYOUT_BITANGENT = 0x5,
		VERTEX_LAYOUT_DUMMY_FLOAT = 0x6,
		VERTEX_LAYOUT_DUMMY_VEC4 = 0x7
	} VertexLayout;

	struct MeshBufferInfo 
	{
		VkBuffer buf = VK_NULL_HANDLE;
		VkDeviceMemory mem = VK_NULL_HANDLE;
		size_t size = 0;
	};

	/** @brief Stores a mesh's vertex and index descriptions */
	struct MeshDescriptor
	{
		uint32_t vertexCount;
		uint32_t indexBase;
		uint32_t indexCount;
	};

	/** @brief Mesh representation storing all data required to generate buffers */
	struct MeshBuffer 
	{
		VkDevice device = VK_NULL_HANDLE;
		std::vector<MeshDescriptor> meshDescriptors;
		MeshBufferInfo vertices;
		MeshBufferInfo indices;
		uint32_t indexCount;
		glm::vec3 dim;
		/** @brief Release all Vulkan resources held by this texture */
		void destroy()
		{
			vkDestroyBuffer(device, vertices.buf, nullptr);
			vkFreeMemory(device, vertices.mem, nullptr);
			if (indices.buf != VK_NULL_HANDLE)
			{
				vkDestroyBuffer(device, indices.buf, nullptr);
				vkFreeMemory(device, indices.mem, nullptr);
			}
		}
	};

	/** @brief Holds parameters for mesh creation */
	struct MeshCreateInfo
	{
		glm::vec3 center;
		glm::vec3 scale;
		glm::vec2 uvscale;
	};

	/** 
	* Get the size of a vertex layout	
	* 
	* @param layout VertexLayout to get the size for
	*
	* @return Size of the vertex layout in bytes
	*/
	static uint32_t vertexSize(std::vector<vkMeshLoader::VertexLayout> layout)
	{
		uint32_t vSize = 0;
		for (auto& layoutDetail : layout)
		{
			switch (layoutDetail)
			{
			// UV only has two components
			case VERTEX_LAYOUT_UV: 
				vSize += 2 * sizeof(float);
				break;
			default:
				vSize += 3 * sizeof(float);
			}
		}
		return vSize;
	}

	/**
	* Generate vertex attribute descriptions for a layout at the given binding point
	*
	* @param layout VertexLayout from which to generate the descriptions 
	* @param attributeDescriptions Refernce to a vector of the descriptions to generate
	* @param binding Index of the attribute description binding point
	*
	* @note Always assumes float formats
	*/
	static void getVertexInputAttributeDescriptions(std::vector<vkMeshLoader::VertexLayout> layout, std::vector<VkVertexInputAttributeDescription> &attributeDescriptions, uint32_t binding)
	{
		uint32_t offset = 0;
		uint32_t location = 0;
		for (auto& layoutDetail : layout)
		{
			VkVertexInputAttributeDescription inputAttribDescription = {};
			inputAttribDescription.binding = binding;
			inputAttribDescription.location = location;
			inputAttribDescription.offset = offset;

			switch (layoutDetail)
			{
			// UV only has two components
			case VERTEX_LAYOUT_UV:
				offset += 2 * sizeof(float);
				inputAttribDescription.format = VK_FORMAT_R32G32_SFLOAT;
				break;
			default:
				offset += 3 * sizeof(float);
				inputAttribDescription.format = VK_FORMAT_R32G32B32_SFLOAT;
			}
			attributeDescriptions.push_back(inputAttribDescription);
			location++;
		}
	}

	// Stores some additonal info and functions for 
	// specifying pipelines, vertex bindings, etc.
	class Mesh
	{
	public:
		MeshBuffer buffers;

		VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
		VkPipeline pipeline = VK_NULL_HANDLE;
		VkDescriptorSet descriptorSet = VK_NULL_HANDLE;

		uint32_t vertexBufferBinding = 0;

		VkPipelineVertexInputStateCreateInfo vertexInputState;
		VkVertexInputBindingDescription bindingDescription;
		std::vector<VkVertexInputAttributeDescription> attributeDescriptions;

		void setupVertexInputState(std::vector<vkMeshLoader::VertexLayout> layout)
		{
			bindingDescription = vkTools::initializers::vertexInputBindingDescription(
				vertexBufferBinding,
				vertexSize(layout),
				VK_VERTEX_INPUT_RATE_VERTEX);

			attributeDescriptions.clear();
			uint32_t offset = 0;
			uint32_t binding = 0;
			for (auto& layoutDetail : layout)
			{
				// Format (layout)
				VkFormat format = (layoutDetail == VERTEX_LAYOUT_UV) ? VK_FORMAT_R32G32_SFLOAT : VK_FORMAT_R32G32B32_SFLOAT;

				attributeDescriptions.push_back(
					vkTools::initializers::vertexInputAttributeDescription(
						vertexBufferBinding,
						binding,
						format,
						offset));

				// Offset
				offset += (layoutDetail == VERTEX_LAYOUT_UV) ? (2 * sizeof(float)) : (3 * sizeof(float));
				binding++;
			}

			vertexInputState = vkTools::initializers::pipelineVertexInputStateCreateInfo();
			vertexInputState.vertexBindingDescriptionCount = 1;
			vertexInputState.pVertexBindingDescriptions = &bindingDescription;
			vertexInputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
			vertexInputState.pVertexAttributeDescriptions = attributeDescriptions.data();
		}

		void drawIndexed(VkCommandBuffer cmdBuffer)
		{
			VkDeviceSize offsets[1] = { 0 };
			if (pipeline != VK_NULL_HANDLE)
			{
				vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
			}
			if ((pipelineLayout != VK_NULL_HANDLE) && (descriptorSet != VK_NULL_HANDLE))
			{
				vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, NULL);
			}
			vkCmdBindVertexBuffers(cmdBuffer, vertexBufferBinding, 1, &buffers.vertices.buf, offsets);
			vkCmdBindIndexBuffer(cmdBuffer, buffers.indices.buf, 0, VK_INDEX_TYPE_UINT32);
			vkCmdDrawIndexed(cmdBuffer, buffers.indexCount, 1, 0, 0, 0);
		}
	};

	static void freeMeshBufferResources(VkDevice device, vkMeshLoader::MeshBuffer *meshBuffer)
	{
		vkDestroyBuffer(device, meshBuffer->vertices.buf, nullptr);
		vkFreeMemory(device, meshBuffer->vertices.mem, nullptr);
		if (meshBuffer->indices.buf != VK_NULL_HANDLE)
		{
			vkDestroyBuffer(device, meshBuffer->indices.buf, nullptr);
			vkFreeMemory(device, meshBuffer->indices.mem, nullptr);
		}
	}
}

// Simple mesh class for getting all the necessary stuff from models loaded via ASSIMP
class VulkanMeshLoader 
{
private:
	vk::VulkanDevice *vulkanDevice;

	static const int defaultFlags = aiProcess_FlipWindingOrder | aiProcess_Triangulate | aiProcess_PreTransformVertices | aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals;

	struct Vertex
	{
		glm::vec3 m_pos;
		glm::vec2 m_tex;
		glm::vec3 m_normal;
		glm::vec3 m_color;
		glm::vec3 m_tangent;
		glm::vec3 m_binormal;

		Vertex() {}

		Vertex(const glm::vec3& pos, const glm::vec2& tex, const glm::vec3& normal, const glm::vec3& tangent, const glm::vec3& bitangent, const glm::vec3& color)
		{
			m_pos = pos;
			m_tex = tex;
			m_normal = normal;
			m_color = color;
			m_tangent = tangent;
			m_binormal = bitangent;
		}
	};

	struct MeshEntry {
		uint32_t NumIndices;
		uint32_t MaterialIndex;
		uint32_t vertexBase;
		std::vector<Vertex> Vertices;
		std::vector<unsigned int> Indices;
	};

public:
#if defined(__ANDROID__)
	AAssetManager* assetManager = nullptr;
#endif

	std::vector<MeshEntry> m_Entries;

	struct Dimension 
	{
		glm::vec3 min = glm::vec3(FLT_MAX);
		glm::vec3 max = glm::vec3(-FLT_MAX);
		glm::vec3 size;
	} dim;

	uint32_t numVertices = 0;

	Assimp::Importer Importer;
	const aiScene* pScene;

	/**
	* Default constructor
	*
	* @param vulkanDevice Pointer to a valid VulkanDevice
	*/
	VulkanMeshLoader(vk::VulkanDevice *vulkanDevice)
	{
		assert(vulkanDevice != nullptr);
		this->vulkanDevice = vulkanDevice;
	}

	/**
	* Default destructor
	*
	* @note Does not free any Vulkan resources
	*/
	~VulkanMeshLoader()
	{
		m_Entries.clear();
	}

	/** 
	* Load a scene from a supported 3D file format
	*
	* @param filename Name of the file (or asset) to load
	* @param flags (Optional) Set of ASSIMP processing flags
	*
	* @return Returns true if the scene has been loaded
	*/
	bool LoadMesh(const std::string& filename, int flags = defaultFlags)
	{
#if defined(__ANDROID__)
		// Meshes are stored inside the apk on Android (compressed)
		// So they need to be loaded via the asset manager

		AAsset* asset = AAssetManager_open(assetManager, filename.c_str(), AASSET_MODE_STREAMING);
		assert(asset);
		size_t size = AAsset_getLength(asset);

		assert(size > 0);
		
		void *meshData = malloc(size);
		AAsset_read(asset, meshData, size);
		AAsset_close(asset);

		pScene = Importer.ReadFileFromMemory(meshData, size, flags);

		free(meshData);
#else
		pScene = Importer.ReadFile(filename.c_str(), flags);
#endif

		if (pScene)
		{
			m_Entries.clear();
			m_Entries.resize(pScene->mNumMeshes);
			// Read in all meshes in the scene
			for (auto i = 0; i < m_Entries.size(); i++)
			{
				m_Entries[i].vertexBase = numVertices;
				numVertices += pScene->mMeshes[i]->mNumVertices;
				const aiMesh* paiMesh = pScene->mMeshes[i];
				InitMesh(&m_Entries[i], paiMesh, pScene);
			}
			return true;
		}
		else 
		{
			printf("Error parsing '%s': '%s'\n", filename.c_str(), Importer.GetErrorString());
#if defined(__ANDROID__)
			LOGE("Error parsing '%s': '%s'", filename.c_str(), Importer.GetErrorString());
#endif
			return false;
		}
	}

	/**
	* Read mesh data from ASSIMP mesh to an internal mesh representation that can be used to generate Vulkan buffers
	*
	* @param meshEntry Pointer to the target MeshEntry strucutre for the mesh data
	* @param paiMesh ASSIMP mesh to get the data from
	* @param pScene Scene file of the ASSIMP mesh
	*/
	void InitMesh(MeshEntry *meshEntry, const aiMesh* paiMesh, const aiScene* pScene)
	{
		meshEntry->MaterialIndex = paiMesh->mMaterialIndex;

		aiColor3D pColor(0.f, 0.f, 0.f);
		pScene->mMaterials[paiMesh->mMaterialIndex]->Get(AI_MATKEY_COLOR_DIFFUSE, pColor);

		aiVector3D Zero3D(0.0f, 0.0f, 0.0f);

		for (unsigned int i = 0; i < paiMesh->mNumVertices; i++) 
		{
			aiVector3D* pPos = &(paiMesh->mVertices[i]);
			aiVector3D* pNormal = &(paiMesh->mNormals[i]);
			aiVector3D* pTexCoord = (paiMesh->HasTextureCoords(0)) ? &(paiMesh->mTextureCoords[0][i]) : &Zero3D;
			aiVector3D* pTangent = (paiMesh->HasTangentsAndBitangents()) ? &(paiMesh->mTangents[i]) : &Zero3D;
			aiVector3D* pBiTangent = (paiMesh->HasTangentsAndBitangents()) ? &(paiMesh->mBitangents[i]) : &Zero3D;

			Vertex v(
				glm::vec3(pPos->x, -pPos->y, pPos->z), 
				glm::vec2(pTexCoord->x , pTexCoord->y),
				glm::vec3(pNormal->x, pNormal->y, pNormal->z),
				glm::vec3(pTangent->x, pTangent->y, pTangent->z),
				glm::vec3(pBiTangent->x, pBiTangent->y, pBiTangent->z),
				glm::vec3(pColor.r, pColor.g, pColor.b)
				);
		
			dim.max.x = fmax(pPos->x, dim.max.x);
			dim.max.y = fmax(pPos->y, dim.max.y);
			dim.max.z = fmax(pPos->z, dim.max.z);

			dim.min.x = fmin(pPos->x, dim.min.x);
			dim.min.y = fmin(pPos->y, dim.min.y);
			dim.min.z = fmin(pPos->z, dim.min.z);

			meshEntry->Vertices.push_back(v);
		}

		dim.size = dim.max - dim.min;

		uint32_t indexBase = static_cast<uint32_t>(meshEntry->Indices.size());
		for (unsigned int i = 0; i < paiMesh->mNumFaces; i++)
		{
			const aiFace& Face = paiMesh->mFaces[i];
			if (Face.mNumIndices != 3)
				continue;
			meshEntry->Indices.push_back(indexBase + Face.mIndices[0]);
			meshEntry->Indices.push_back(indexBase + Face.mIndices[1]);
			meshEntry->Indices.push_back(indexBase + Face.mIndices[2]);
		}
	}

	/**
	* Create Vulkan buffers for the index and vertex buffer using a vertex layout
	*
	* @note Only does staging if a valid command buffer and transfer queue are passed
	*
	* @param meshBuffer Pointer to the mesh buffer containing buffer handles and memory
	* @param layout Vertex layout for the vertex buffer
	* @param createInfo Structure containing information for mesh creation time (center, scaling, etc.)
	* @param useStaging If true, buffers are staged to device local memory
	* @param copyCmd (Required for staging) Command buffer to put the copy commands into
	* @param copyQueue (Required for staging) Queue to put copys into
	*/
	void createBuffers(
		vkMeshLoader::MeshBuffer *meshBuffer,
		std::vector<vkMeshLoader::VertexLayout> layout,
		vkMeshLoader::MeshCreateInfo *createInfo,
		bool useStaging,
		VkQueue copyQueue)
	{
		glm::vec3 scale;
		glm::vec2 uvscale;
		glm::vec3 center;
		if (createInfo == nullptr)
		{
			scale = glm::vec3(1.0f);
			uvscale = glm::vec2(1.0f);
			center = glm::vec3(0.0f);
		}
		else
		{
			scale = createInfo->scale;
			uvscale = createInfo->uvscale;
			center = createInfo->center;
		}

		std::vector<float> vertexBuffer;
		for (int m = 0; m < m_Entries.size(); m++)
		{
			for (int i = 0; i < m_Entries[m].Vertices.size(); i++)
			{
				// Push vertex data depending on layout
				for (auto& layoutDetail : layout)
				{
					// Position
					if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_POSITION)
					{
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_pos.x * scale.x + center.x);
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_pos.y * scale.y + center.y);
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_pos.z * scale.z + center.z);
					}
					// Normal
					if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_NORMAL)
					{
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_normal.x);
						vertexBuffer.push_back(-m_Entries[m].Vertices[i].m_normal.y);
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_normal.z);
					}
					// Texture coordinates
					if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_UV)
					{
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tex.s * uvscale.s);
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tex.t * uvscale.t);
					}
					// Color
					if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_COLOR)
					{
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_color.r);
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_color.g);
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_color.b);
					}
					// Tangent
					if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_TANGENT)
					{
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tangent.x);
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tangent.y);
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tangent.z);
					}
					// Bitangent
					if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_BITANGENT)
					{
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_binormal.x);
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_binormal.y);
						vertexBuffer.push_back(m_Entries[m].Vertices[i].m_binormal.z);
					}
					// Dummy layout components for padding
					if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_DUMMY_FLOAT)
					{
						vertexBuffer.push_back(0.0f);
					}
					if (layoutDetail == vkMeshLoader::VERTEX_LAYOUT_DUMMY_VEC4)
					{
						vertexBuffer.push_back(0.0f);
						vertexBuffer.push_back(0.0f);
						vertexBuffer.push_back(0.0f);
						vertexBuffer.push_back(0.0f);
					}
				}
			}
		}
		meshBuffer->vertices.size = vertexBuffer.size() * sizeof(float);

		dim.min *= scale;
		dim.max *= scale;
		dim.size *= scale;

		std::vector<uint32_t> indexBuffer;
		for (uint32_t m = 0; m < m_Entries.size(); m++)
		{
			uint32_t indexBase = static_cast<uint32_t>(indexBuffer.size());
			for (uint32_t i = 0; i < m_Entries[m].Indices.size(); i++)
			{
				indexBuffer.push_back(m_Entries[m].Indices[i] + indexBase);
			}
			vkMeshLoader::MeshDescriptor descriptor{};
			descriptor.indexBase = indexBase;
			descriptor.indexCount = static_cast<uint32_t>(m_Entries[m].Indices.size());
			descriptor.vertexCount = static_cast<uint32_t>(m_Entries[m].Vertices.size());
			meshBuffer->meshDescriptors.push_back(descriptor);
		}
		meshBuffer->indices.size = indexBuffer.size() * sizeof(uint32_t);
		meshBuffer->indexCount = static_cast<uint32_t>(indexBuffer.size());
		meshBuffer->device = vulkanDevice->logicalDevice;

		// Use staging buffer to move vertex and index buffer to device local memory
		if (useStaging && copyQueue != VK_NULL_HANDLE)
		{
			// Create staging buffers
			struct {
				VkBuffer buffer;
				VkDeviceMemory memory;
			} vertexStaging, indexStaging;

			// Vertex buffer
			vulkanDevice->createBuffer(
				VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
				VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
				meshBuffer->vertices.size,
				&vertexStaging.buffer,
				&vertexStaging.memory,
				vertexBuffer.data());

			// Index buffer
			vulkanDevice->createBuffer(
				VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
				VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
				meshBuffer->indices.size,
				&indexStaging.buffer,
				&indexStaging.memory,
				indexBuffer.data());

			// Create device local target buffers
			// Vertex buffer
			vulkanDevice->createBuffer(
				VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
				VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
				meshBuffer->vertices.size,
				&meshBuffer->vertices.buf,
				&meshBuffer->vertices.mem);

			// Index buffer
			vulkanDevice->createBuffer(
				VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
				VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
				meshBuffer->indices.size,
				&meshBuffer->indices.buf,
				&meshBuffer->indices.mem);

			// Copy from staging buffers
			VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);

			VkBufferCopy copyRegion = {};

			copyRegion.size = meshBuffer->vertices.size;
			vkCmdCopyBuffer(
				copyCmd,
				vertexStaging.buffer,
				meshBuffer->vertices.buf,
				1,
				&copyRegion);

			copyRegion.size = meshBuffer->indices.size;
			vkCmdCopyBuffer(
				copyCmd,
				indexStaging.buffer,
				meshBuffer->indices.buf,
				1,
				&copyRegion);

			vulkanDevice->flushCommandBuffer(copyCmd, copyQueue);

			vkDestroyBuffer(vulkanDevice->logicalDevice, vertexStaging.buffer, nullptr);
			vkFreeMemory(vulkanDevice->logicalDevice, vertexStaging.memory, nullptr);
			vkDestroyBuffer(vulkanDevice->logicalDevice, indexStaging.buffer, nullptr);
			vkFreeMemory(vulkanDevice->logicalDevice, indexStaging.memory, nullptr);
		}
		else
		{
			// Generate vertex buffer
			vulkanDevice->createBuffer(
				VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
				VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
				meshBuffer->vertices.size,
				&meshBuffer->vertices.buf,
				&meshBuffer->vertices.mem,
				vertexBuffer.data());

			// Generate index buffer
			vulkanDevice->createBuffer(
				VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
				VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
				meshBuffer->indices.size,
				&meshBuffer->indices.buf,
				&meshBuffer->indices.mem,
				indexBuffer.data());
		}
	}
};
\ No newline at end of file

A src/vulkanTextureLoader.hpp => src/vulkanTextureLoader.hpp +1083 -0
@@ 0,0 1,1083 @@
/*
* Texture loader for Vulkan
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/

#pragma once

#include <string>
#include <iostream>

#include <vulkan/vulkan.h>
#include <gli/gli.hpp>

#include "vulkandevice.hpp"

#if defined(__ANDROID__)
#include <android/asset_manager.h>
#endif

namespace vkTools 
{
	/**
	* @brief Encapsulates a Vulkan texture object (including view, sampler, descriptor, etc.)
	*/
	struct VulkanTexture
	{
		VkDevice device;
		VkSampler sampler;
		VkImage image;
		VkImageLayout imageLayout;
		VkDeviceMemory deviceMemory;
		VkImageView view;
		uint32_t width, height;
		uint32_t mipLevels;
		uint32_t layerCount;
		VkDescriptorImageInfo descriptor;

		/** @brief Update image descriptor from current sampler, view and image layout */
		void updateDescriptor()
		{
			descriptor.sampler = sampler;
			descriptor.imageView = view;
			descriptor.imageLayout = imageLayout;
		}

		/** @brief Release all Vulkan resources held by this texture */
		void destroy()
		{
			vkDestroyImageView(device, view, nullptr);
			vkDestroyImage(device, image, nullptr);
			if (sampler)
			{
				vkDestroySampler(device, sampler, nullptr);
			}
			vkFreeMemory(device, deviceMemory, nullptr);
		}
	};

	/**
	* @brief A simple Vulkan texture uploader for getting images into GPU memory
	*/
	class VulkanTextureLoader
	{
	private:
		vk::VulkanDevice *vulkanDevice;
		VkQueue queue;
		VkCommandBuffer cmdBuffer;
		VkCommandPool cmdPool;
	public:
		/**
		* Default constructor
		*
		* @param vulkanDevice Pointer to a valid VulkanDevice
		* @param queue Queue for the copy commands when using staging (queue must support transfers)
		* @param cmdPool Commandpool used to get command buffers for copies and layout transitions
		*/
		VulkanTextureLoader(vk::VulkanDevice *vulkanDevice, VkQueue queue, VkCommandPool cmdPool)
		{
			this->vulkanDevice = vulkanDevice;
			this->queue = queue;
			this->cmdPool = cmdPool;

			// Create command buffer for submitting image barriers
			// and converting tilings
			VkCommandBufferAllocateInfo cmdBufInfo = {};
			cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
			cmdBufInfo.commandPool = cmdPool;
			cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
			cmdBufInfo.commandBufferCount = 1;

			VK_CHECK_RESULT(vkAllocateCommandBuffers(vulkanDevice->logicalDevice, &cmdBufInfo, &cmdBuffer));
		}

		/**
		* Default destructor
		*
		* @note Does not free texture resources
		*/
		~VulkanTextureLoader()
		{
			vkFreeCommandBuffers(vulkanDevice->logicalDevice, cmdPool, 1, &cmdBuffer);
		}

		/**
		* Load a 2D texture including all mip levels
		*
		* @param filename File to load
		* @param format Vulkan format of the image data stored in the file
		* @param texture Pointer to the texture object to load the image into 
		* @param (Optional) forceLinear Force linear tiling (not advised, defaults to false)
		* @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT)
		* @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
		*
		* @note Only supports .ktx and .dds
		*/
		void loadTexture(
			std::string filename, 
			VkFormat format, 
			VulkanTexture *texture, 
			bool forceLinear = false, 
			VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT,
			VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
		{
#if defined(__ANDROID__)
			// Textures are stored inside the apk on Android (compressed)
			// So they need to be loaded via the asset manager
			AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING);
			assert(asset);
			size_t size = AAsset_getLength(asset);
			assert(size > 0);

			void *textureData = malloc(size);
			AAsset_read(asset, textureData, size);
			AAsset_close(asset);

			gli::texture2d tex2D(gli::load((const char*)textureData, size));

			free(textureData);
#else
			gli::texture2d tex2D(gli::load(filename.c_str()));
#endif		
			assert(!tex2D.empty());

			texture->device = vulkanDevice->logicalDevice;
			texture->width = static_cast<uint32_t>(tex2D[0].extent().x);
			texture->height = static_cast<uint32_t>(tex2D[0].extent().y);
			texture->mipLevels = static_cast<uint32_t>(tex2D.levels());

			// Get device properites for the requested texture format
			VkFormatProperties formatProperties;
			vkGetPhysicalDeviceFormatProperties(vulkanDevice->physicalDevice, format, &formatProperties);

			// Only use linear tiling if requested (and supported by the device)
			// Support for linear tiling is mostly limited, so prefer to use
			// optimal tiling instead
			// On most implementations linear tiling will only support a very
			// limited amount of formats and features (mip maps, cubemaps, arrays, etc.)
			VkBool32 useStaging = !forceLinear;

			VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo();
			VkMemoryRequirements memReqs;

			// Use a separate command buffer for texture loading
			VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
			VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo));

			if (useStaging)
			{
				// Create a host-visible staging buffer that contains the raw image data
				VkBuffer stagingBuffer;
				VkDeviceMemory stagingMemory;

				VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo();
				bufferCreateInfo.size = tex2D.size();
				// This buffer is used as a transfer source for the buffer copy
				bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
				bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

				VK_CHECK_RESULT(vkCreateBuffer(vulkanDevice->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));

				// Get memory requirements for the staging buffer (alignment, memory type bits)
				vkGetBufferMemoryRequirements(vulkanDevice->logicalDevice, stagingBuffer, &memReqs);

				memAllocInfo.allocationSize = memReqs.size;
				// Get memory type index for a host visible buffer
				memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

				VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memAllocInfo, nullptr, &stagingMemory));
				VK_CHECK_RESULT(vkBindBufferMemory(vulkanDevice->logicalDevice, stagingBuffer, stagingMemory, 0));

				// Copy texture data into staging buffer
				uint8_t *data;
				VK_CHECK_RESULT(vkMapMemory(vulkanDevice->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data));
				memcpy(data, tex2D.data(), tex2D.size());
				vkUnmapMemory(vulkanDevice->logicalDevice, stagingMemory);

				// Setup buffer copy regions for each mip level
				std::vector<VkBufferImageCopy> bufferCopyRegions;
				uint32_t offset = 0;

				for (uint32_t i = 0; i < texture->mipLevels; i++)
				{
					VkBufferImageCopy bufferCopyRegion = {};
					bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
					bufferCopyRegion.imageSubresource.mipLevel = i;
					bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
					bufferCopyRegion.imageSubresource.layerCount = 1;
					bufferCopyRegion.imageExtent.width = static_cast<uint32_t>(tex2D[i].extent().x);
					bufferCopyRegion.imageExtent.height = static_cast<uint32_t>(tex2D[i].extent().y);
					bufferCopyRegion.imageExtent.depth = 1;
					bufferCopyRegion.bufferOffset = offset;

					bufferCopyRegions.push_back(bufferCopyRegion);

					offset += static_cast<uint32_t>(tex2D[i].size());
				}

				// Create optimal tiled target image
				VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo();
				imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
				imageCreateInfo.format = format;
				imageCreateInfo.mipLevels = texture->mipLevels;
				imageCreateInfo.arrayLayers = 1;
				imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
				imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
				imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
				imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
				imageCreateInfo.extent = { texture->width, texture->height, 1 };
				imageCreateInfo.usage = imageUsageFlags;
				// Ensure that the TRANSFER_DST bit is set for staging
				if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
				{
					imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
				}
				VK_CHECK_RESULT(vkCreateImage(vulkanDevice->logicalDevice, &imageCreateInfo, nullptr, &texture->image));

				vkGetImageMemoryRequirements(vulkanDevice->logicalDevice, texture->image, &memReqs);

				memAllocInfo.allocationSize = memReqs.size;

				memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
				VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memAllocInfo, nullptr, &texture->deviceMemory));
				VK_CHECK_RESULT(vkBindImageMemory(vulkanDevice->logicalDevice, texture->image, texture->deviceMemory, 0));

				VkImageSubresourceRange subresourceRange = {};
				subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
				subresourceRange.baseMipLevel = 0;
				subresourceRange.levelCount = texture->mipLevels;
				subresourceRange.layerCount = 1;

				// Image barrier for optimal image (target)
				// Optimal image will be used as destination for the copy
				setImageLayout(
					cmdBuffer,
					texture->image,
					VK_IMAGE_ASPECT_COLOR_BIT,
					VK_IMAGE_LAYOUT_UNDEFINED,
					VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
					subresourceRange);

				// Copy mip levels from staging buffer
				vkCmdCopyBufferToImage(
					cmdBuffer,
					stagingBuffer,
					texture->image,
					VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
					static_cast<uint32_t>(bufferCopyRegions.size()),
					bufferCopyRegions.data()
					);

				// Change texture image layout to shader read after all mip levels have been copied
				texture->imageLayout = imageLayout;
				setImageLayout(
					cmdBuffer,
					texture->image,
					VK_IMAGE_ASPECT_COLOR_BIT,
					VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
					texture->imageLayout,
					subresourceRange);

				// Submit command buffer containing copy and image layout commands
				VK_CHECK_RESULT(vkEndCommandBuffer(cmdBuffer));

				// Create a fence to make sure that the copies have finished before continuing
				VkFence copyFence;
				VkFenceCreateInfo fenceCreateInfo = vkTools::initializers::fenceCreateInfo(VK_FLAGS_NONE);
				VK_CHECK_RESULT(vkCreateFence(vulkanDevice->logicalDevice, &fenceCreateInfo, nullptr, &copyFence));

				VkSubmitInfo submitInfo = vkTools::initializers::submitInfo();
				submitInfo.commandBufferCount = 1;
				submitInfo.pCommandBuffers = &cmdBuffer;

				VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, copyFence));

				VK_CHECK_RESULT(vkWaitForFences(vulkanDevice->logicalDevice, 1, &copyFence, VK_TRUE, DEFAULT_FENCE_TIMEOUT));

				vkDestroyFence(vulkanDevice->logicalDevice, copyFence, nullptr);

				// Clean up staging resources
				vkFreeMemory(vulkanDevice->logicalDevice, stagingMemory, nullptr);
				vkDestroyBuffer(vulkanDevice->logicalDevice, stagingBuffer, nullptr);
			}
			else
			{
				// Prefer using optimal tiling, as linear tiling 
				// may support only a small set of features 
				// depending on implementation (e.g. no mip maps, only one layer, etc.)

				// Check if this support is supported for linear tiling
				assert(formatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);

				VkImage mappableImage;
				VkDeviceMemory mappableMemory;

				VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo();
				imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
				imageCreateInfo.format = format;
				imageCreateInfo.extent = { texture->width, texture->height, 1 };
				imageCreateInfo.mipLevels = 1;
				imageCreateInfo.arrayLayers = 1;
				imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
				imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
				imageCreateInfo.usage = imageUsageFlags;
				imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
				imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;

				// Load mip map level 0 to linear tiling image
				VK_CHECK_RESULT(vkCreateImage(vulkanDevice->logicalDevice, &imageCreateInfo, nullptr, &mappableImage));

				// Get memory requirements for this image 
				// like size and alignment
				vkGetImageMemoryRequirements(vulkanDevice->logicalDevice, mappableImage, &memReqs);
				// Set memory allocation size to required memory size
				memAllocInfo.allocationSize = memReqs.size;

				// Get memory type that can be mapped to host memory
				memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

				// Allocate host memory
				VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memAllocInfo, nullptr, &mappableMemory));

				// Bind allocated image for use
				VK_CHECK_RESULT(vkBindImageMemory(vulkanDevice->logicalDevice, mappableImage, mappableMemory, 0));

				// Get sub resource layout
				// Mip map count, array layer, etc.
				VkImageSubresource subRes = {};
				subRes.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
				subRes.mipLevel = 0;

				VkSubresourceLayout subResLayout;
				void *data;

				// Get sub resources layout 
				// Includes row pitch, size offsets, etc.
				vkGetImageSubresourceLayout(vulkanDevice->logicalDevice, mappableImage, &subRes, &subResLayout);

				// Map image memory
				VK_CHECK_RESULT(vkMapMemory(vulkanDevice->logicalDevice, mappableMemory, 0, memReqs.size, 0, &data));

				// Copy image data into memory
				memcpy(data, tex2D[subRes.mipLevel].data(), tex2D[subRes.mipLevel].size());

				vkUnmapMemory(vulkanDevice->logicalDevice, mappableMemory);

				// Linear tiled images don't need to be staged
				// and can be directly used as textures
				texture->image = mappableImage;
				texture->deviceMemory = mappableMemory;
				texture->imageLayout = imageLayout;

				// Setup image memory barrier
				setImageLayout(
					cmdBuffer,
					texture->image, 
					VK_IMAGE_ASPECT_COLOR_BIT, 
					VK_IMAGE_LAYOUT_PREINITIALIZED, 
					texture->imageLayout);

				// Submit command buffer containing copy and image layout commands
				VK_CHECK_RESULT(vkEndCommandBuffer(cmdBuffer));

				VkFence nullFence = { VK_NULL_HANDLE };

				VkSubmitInfo submitInfo = vkTools::initializers::submitInfo();
				submitInfo.waitSemaphoreCount = 0;
				submitInfo.commandBufferCount = 1;
				submitInfo.pCommandBuffers = &cmdBuffer;

				VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, nullFence));
				VK_CHECK_RESULT(vkQueueWaitIdle(queue));
			}

			// Create sampler
			VkSamplerCreateInfo sampler = {};
			sampler.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
			sampler.magFilter = VK_FILTER_LINEAR;
			sampler.minFilter = VK_FILTER_LINEAR;
			sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
			sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
			sampler.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
			sampler.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
			sampler.mipLodBias = 0.0f;
			sampler.compareOp = VK_COMPARE_OP_NEVER;
			sampler.minLod = 0.0f;
			// Max level-of-detail should match mip level count
			sampler.maxLod = (useStaging) ? (float)texture->mipLevels : 0.0f;
			// Enable anisotropic filtering
			sampler.maxAnisotropy = 8;
			sampler.anisotropyEnable = VK_TRUE;
			sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
			VK_CHECK_RESULT(vkCreateSampler(vulkanDevice->logicalDevice, &sampler, nullptr, &texture->sampler));
			
			// Create image view
			// Textures are not directly accessed by the shaders and
			// are abstracted by image views containing additional
			// information and sub resource ranges
			VkImageViewCreateInfo view = {};
			view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
			view.pNext = NULL;
			view.viewType = VK_IMAGE_VIEW_TYPE_2D;
			view.format = format;
			view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
			view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
			// Linear tiling usually won't support mip maps
			// Only set mip map count if optimal tiling is used
			view.subresourceRange.levelCount = (useStaging) ? texture->mipLevels : 1;
			view.image = texture->image;
			VK_CHECK_RESULT(vkCreateImageView(vulkanDevice->logicalDevice, &view, nullptr, &texture->view));

			// Update descriptor image info member that can be used for setting up descriptor sets
			texture->updateDescriptor();
		}

		/**
		* Load a cubemap texture including all mip levels from a single file
		*
		* @param filename File to load
		* @param format Vulkan format of the image data stored in the file
		* @param texture Pointer to the texture object to load the image into
		* @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT)
		* @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
		*
		* @note Only supports .ktx and .dds
		*/
		void loadCubemap(
			std::string filename, 
			VkFormat format, 
			VulkanTexture *texture, 
			VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT,
			VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
		{
#if defined(__ANDROID__)
			// Textures are stored inside the apk on Android (compressed)
			// So they need to be loaded via the asset manager
			AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING);
			assert(asset);
			size_t size = AAsset_getLength(asset);
			assert(size > 0);

			void *textureData = malloc(size);
			AAsset_read(asset, textureData, size);
			AAsset_close(asset);

			gli::texture_cube texCube(gli::load((const char*)textureData, size));

			free(textureData);
#else
			gli::texture_cube texCube(gli::load(filename));
#endif	
			assert(!texCube.empty());

			texture->device = vulkanDevice->logicalDevice;
			texture->width = static_cast<uint32_t>(texCube.extent().x);
			texture->height = static_cast<uint32_t>(texCube.extent().y);
			texture->mipLevels = static_cast<uint32_t>(texCube.levels());

			VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo();
			VkMemoryRequirements memReqs;

			// Create a host-visible staging buffer that contains the raw image data
			VkBuffer stagingBuffer;
			VkDeviceMemory stagingMemory;

			VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo();
			bufferCreateInfo.size = texCube.size();
			// This buffer is used as a transfer source for the buffer copy
			bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
			bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

			VK_CHECK_RESULT(vkCreateBuffer(vulkanDevice->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));

			// Get memory requirements for the staging buffer (alignment, memory type bits)
			vkGetBufferMemoryRequirements(vulkanDevice->logicalDevice, stagingBuffer, &memReqs);

			memAllocInfo.allocationSize = memReqs.size;
			// Get memory type index for a host visible buffer
			memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

			VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memAllocInfo, nullptr, &stagingMemory));
			VK_CHECK_RESULT(vkBindBufferMemory(vulkanDevice->logicalDevice, stagingBuffer, stagingMemory, 0));

			// Copy texture data into staging buffer
			uint8_t *data;
			VK_CHECK_RESULT(vkMapMemory(vulkanDevice->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data));
			memcpy(data, texCube.data(), texCube.size());
			vkUnmapMemory(vulkanDevice->logicalDevice, stagingMemory);

			// Setup buffer copy regions for each face including all of it's miplevels
			std::vector<VkBufferImageCopy> bufferCopyRegions;
			size_t offset = 0;

			for (uint32_t face = 0; face < 6; face++)
			{
				for (uint32_t level = 0; level < texture->mipLevels; level++)
				{
					VkBufferImageCopy bufferCopyRegion = {};
					bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
					bufferCopyRegion.imageSubresource.mipLevel = level;
					bufferCopyRegion.imageSubresource.baseArrayLayer = face;
					bufferCopyRegion.imageSubresource.layerCount = 1;
					bufferCopyRegion.imageExtent.width = static_cast<uint32_t>(texCube[face][level].extent().x);
					bufferCopyRegion.imageExtent.height = static_cast<uint32_t>(texCube[face][level].extent().y);
					bufferCopyRegion.imageExtent.depth = 1;
					bufferCopyRegion.bufferOffset = offset;

					bufferCopyRegions.push_back(bufferCopyRegion);

					// Increase offset into staging buffer for next level / face
					offset += texCube[face][level].size();
				}
			}

			// Create optimal tiled target image
			VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo();
			imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
			imageCreateInfo.format = format;
			imageCreateInfo.mipLevels = texture->mipLevels;
			imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
			imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
			imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
			imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
			imageCreateInfo.extent = { texture->width, texture->height, 1 };
			imageCreateInfo.usage = imageUsageFlags;
			// Ensure that the TRANSFER_DST bit is set for staging
			if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
			{
				imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
			}
			// Cube faces count as array layers in Vulkan
			imageCreateInfo.arrayLayers = 6;
			// This flag is required for cube map images
			imageCreateInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;


			VK_CHECK_RESULT(vkCreateImage(vulkanDevice->logicalDevice, &imageCreateInfo, nullptr, &texture->image));

			vkGetImageMemoryRequirements(vulkanDevice->logicalDevice, texture->image, &memReqs);

			memAllocInfo.allocationSize = memReqs.size;
			memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

			VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memAllocInfo, nullptr, &texture->deviceMemory));
			VK_CHECK_RESULT(vkBindImageMemory(vulkanDevice->logicalDevice, texture->image, texture->deviceMemory, 0));

			VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
			VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo));

			// Image barrier for optimal image (target)
			// Set initial layout for all array layers (faces) of the optimal (target) tiled texture
			VkImageSubresourceRange subresourceRange = {};
			subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
			subresourceRange.baseMipLevel = 0;
			subresourceRange.levelCount = texture->mipLevels;
			subresourceRange.layerCount = 6;

			vkTools::setImageLayout(
				cmdBuffer,
				texture->image,
				VK_IMAGE_ASPECT_COLOR_BIT,
				VK_IMAGE_LAYOUT_UNDEFINED,
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				subresourceRange);

			// Copy the cube map faces from the staging buffer to the optimal tiled image
			vkCmdCopyBufferToImage(
				cmdBuffer,
				stagingBuffer,
				texture->image,
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				static_cast<uint32_t>(bufferCopyRegions.size()),
				bufferCopyRegions.data());

			// Change texture image layout to shader read after all faces have been copied
			texture->imageLayout = imageLayout;
			vkTools::setImageLayout(
				cmdBuffer,
				texture->image,
				VK_IMAGE_ASPECT_COLOR_BIT,
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				texture->imageLayout,
				subresourceRange);

			VK_CHECK_RESULT(vkEndCommandBuffer(cmdBuffer));

			// Create a fence to make sure that the copies have finished before continuing
			VkFence copyFence;
			VkFenceCreateInfo fenceCreateInfo = vkTools::initializers::fenceCreateInfo(VK_FLAGS_NONE);
			VK_CHECK_RESULT(vkCreateFence(vulkanDevice->logicalDevice, &fenceCreateInfo, nullptr, &copyFence));

			VkSubmitInfo submitInfo = vkTools::initializers::submitInfo();
			submitInfo.commandBufferCount = 1;
			submitInfo.pCommandBuffers = &cmdBuffer;

			VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, copyFence));

			VK_CHECK_RESULT(vkWaitForFences(vulkanDevice->logicalDevice, 1, &copyFence, VK_TRUE, DEFAULT_FENCE_TIMEOUT));

			vkDestroyFence(vulkanDevice->logicalDevice, copyFence, nullptr);

			// Create sampler
			VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo();
			sampler.magFilter = VK_FILTER_LINEAR;
			sampler.minFilter = VK_FILTER_LINEAR;
			sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
			sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
			sampler.addressModeV = sampler.addressModeU;
			sampler.addressModeW = sampler.addressModeU;
			sampler.mipLodBias = 0.0f;
			sampler.maxAnisotropy = 8;
			sampler.compareOp = VK_COMPARE_OP_NEVER;
			sampler.minLod = 0.0f;
			sampler.maxLod = (float)texture->mipLevels;
			sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
			VK_CHECK_RESULT(vkCreateSampler(vulkanDevice->logicalDevice, &sampler, nullptr, &texture->sampler));

			// Create image view
			VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo();
			view.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
			view.format = format;
			view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
			view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
			view.subresourceRange.layerCount = 6;
			view.subresourceRange.levelCount = texture->mipLevels;
			view.image = texture->image;
			VK_CHECK_RESULT(vkCreateImageView(vulkanDevice->logicalDevice, &view, nullptr, &texture->view));

			// Clean up staging resources
			vkFreeMemory(vulkanDevice->logicalDevice, stagingMemory, nullptr);
			vkDestroyBuffer(vulkanDevice->logicalDevice, stagingBuffer, nullptr);

			// Update descriptor image info member that can be used for setting up descriptor sets
			texture->updateDescriptor();
		}

		/**
		* Load a texture array including all mip levels from a single file
		*
		* @param filename File to load
		* @param format Vulkan format of the image data stored in the file
		* @param texture Pointer to the texture object to load the image into
		* @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT)
		* @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
		*
		* @note Only supports .ktx and .dds
		*/
		void loadTextureArray(
			std::string filename, 
			VkFormat format, 
			VulkanTexture *texture, 
			VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT,
			VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
		{
#if defined(__ANDROID__)
			// Textures are stored inside the apk on Android (compressed)
			// So they need to be loaded via the asset manager
			AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING);
			assert(asset);
			size_t size = AAsset_getLength(asset);
			assert(size > 0);

			void *textureData = malloc(size);
			AAsset_read(asset, textureData, size);
			AAsset_close(asset);

			gli::texture2d_array tex2DArray(gli::load((const char*)textureData, size));

			free(textureData);
#else
			gli::texture2d_array tex2DArray(gli::load(filename));
#endif	

			assert(!tex2DArray.empty());

			texture->device = vulkanDevice->logicalDevice;
			texture->width = static_cast<uint32_t>(tex2DArray.extent().x);
			texture->height = static_cast<uint32_t>(tex2DArray.extent().y);
			texture->layerCount = static_cast<uint32_t>(tex2DArray.layers());
			texture->mipLevels = static_cast<uint32_t>(tex2DArray.levels());

			VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo();
			VkMemoryRequirements memReqs;

			// Create a host-visible staging buffer that contains the raw image data
			VkBuffer stagingBuffer;
			VkDeviceMemory stagingMemory;

			VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo();
			bufferCreateInfo.size = tex2DArray.size();
			// This buffer is used as a transfer source for the buffer copy
			bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
			bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

			VK_CHECK_RESULT(vkCreateBuffer(vulkanDevice->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));

			// Get memory requirements for the staging buffer (alignment, memory type bits)
			vkGetBufferMemoryRequirements(vulkanDevice->logicalDevice, stagingBuffer, &memReqs);

			memAllocInfo.allocationSize = memReqs.size;
			// Get memory type index for a host visible buffer
			memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

			VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memAllocInfo, nullptr, &stagingMemory));
			VK_CHECK_RESULT(vkBindBufferMemory(vulkanDevice->logicalDevice, stagingBuffer, stagingMemory, 0));

			// Copy texture data into staging buffer
			uint8_t *data;
			VK_CHECK_RESULT(vkMapMemory(vulkanDevice->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data));
			memcpy(data, tex2DArray.data(), static_cast<size_t>(tex2DArray.size()));
			vkUnmapMemory(vulkanDevice->logicalDevice, stagingMemory);

			// Setup buffer copy regions for each layer including all of it's miplevels
			std::vector<VkBufferImageCopy> bufferCopyRegions;
			size_t offset = 0;

			for (uint32_t layer = 0; layer < texture->layerCount; layer++)
			{
				for (uint32_t level = 0; level < texture->mipLevels; level++)
				{
					VkBufferImageCopy bufferCopyRegion = {};
					bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
					bufferCopyRegion.imageSubresource.mipLevel = level;
					bufferCopyRegion.imageSubresource.baseArrayLayer = layer;
					bufferCopyRegion.imageSubresource.layerCount = 1;
					bufferCopyRegion.imageExtent.width = static_cast<uint32_t>(tex2DArray[layer][level].extent().x);
					bufferCopyRegion.imageExtent.height = static_cast<uint32_t>(tex2DArray[layer][level].extent().y);
					bufferCopyRegion.imageExtent.depth = 1;
					bufferCopyRegion.bufferOffset = offset;

					bufferCopyRegions.push_back(bufferCopyRegion);

					// Increase offset into staging buffer for next level / face
					offset += tex2DArray[layer][level].size();
				}
			}

			// Create optimal tiled target image
			VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo();
			imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
			imageCreateInfo.format = format;
			imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
			imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
			imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
			imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
			imageCreateInfo.extent = { texture->width, texture->height, 1 };
			imageCreateInfo.usage = imageUsageFlags;
			// Ensure that the TRANSFER_DST bit is set for staging
			if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
			{
				imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
			}
			imageCreateInfo.arrayLayers = texture->layerCount;
			imageCreateInfo.mipLevels = texture->mipLevels;

			VK_CHECK_RESULT(vkCreateImage(vulkanDevice->logicalDevice, &imageCreateInfo, nullptr, &texture->image));

			vkGetImageMemoryRequirements(vulkanDevice->logicalDevice, texture->image, &memReqs);

			memAllocInfo.allocationSize = memReqs.size;
			memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

			VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memAllocInfo, nullptr, &texture->deviceMemory));
			VK_CHECK_RESULT(vkBindImageMemory(vulkanDevice->logicalDevice, texture->image, texture->deviceMemory, 0));

			VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
			VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo));

			// Image barrier for optimal image (target)
			// Set initial layout for all array layers (faces) of the optimal (target) tiled texture
			VkImageSubresourceRange subresourceRange = {};
			subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
			subresourceRange.baseMipLevel = 0;
			subresourceRange.levelCount = texture->mipLevels;
			subresourceRange.layerCount = texture->layerCount;

			vkTools::setImageLayout(
				cmdBuffer,
				texture->image,
				VK_IMAGE_ASPECT_COLOR_BIT,
				VK_IMAGE_LAYOUT_UNDEFINED,
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				subresourceRange);

			// Copy the layers and mip levels from the staging buffer to the optimal tiled image
			vkCmdCopyBufferToImage(
				cmdBuffer,
				stagingBuffer,
				texture->image,
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				static_cast<uint32_t>(bufferCopyRegions.size()),
				bufferCopyRegions.data());

			// Change texture image layout to shader read after all faces have been copied
			texture->imageLayout = imageLayout;
			vkTools::setImageLayout(
				cmdBuffer,
				texture->image,
				VK_IMAGE_ASPECT_COLOR_BIT,
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				texture->imageLayout,
				subresourceRange);

			VK_CHECK_RESULT(vkEndCommandBuffer(cmdBuffer));

			// Create a fence to make sure that the copies have finished before continuing
			VkFence copyFence;
			VkFenceCreateInfo fenceCreateInfo = vkTools::initializers::fenceCreateInfo(VK_FLAGS_NONE);
			VK_CHECK_RESULT(vkCreateFence(vulkanDevice->logicalDevice, &fenceCreateInfo, nullptr, &copyFence));

			VkSubmitInfo submitInfo = vkTools::initializers::submitInfo();
			submitInfo.commandBufferCount = 1;
			submitInfo.pCommandBuffers = &cmdBuffer;

			VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, copyFence));

			VK_CHECK_RESULT(vkWaitForFences(vulkanDevice->logicalDevice, 1, &copyFence, VK_TRUE, DEFAULT_FENCE_TIMEOUT));

			vkDestroyFence(vulkanDevice->logicalDevice, copyFence, nullptr);

			// Create sampler
			VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo();
			sampler.magFilter = VK_FILTER_LINEAR;
			sampler.minFilter = VK_FILTER_LINEAR;
			sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
			sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
			sampler.addressModeV = sampler.addressModeU;
			sampler.addressModeW = sampler.addressModeU;
			sampler.mipLodBias = 0.0f;
			sampler.maxAnisotropy = 8;
			sampler.compareOp = VK_COMPARE_OP_NEVER;
			sampler.minLod = 0.0f;
			sampler.maxLod = (float)texture->mipLevels;
			sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
			VK_CHECK_RESULT(vkCreateSampler(vulkanDevice->logicalDevice, &sampler, nullptr, &texture->sampler));

			// Create image view
			VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo();
			view.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
			view.format = format;
			view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
			view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
			view.subresourceRange.layerCount = texture->layerCount;
			view.subresourceRange.levelCount = texture->mipLevels;
			view.image = texture->image;
			VK_CHECK_RESULT(vkCreateImageView(vulkanDevice->logicalDevice, &view, nullptr, &texture->view));

			// Clean up staging resources
			vkFreeMemory(vulkanDevice->logicalDevice, stagingMemory, nullptr);
			vkDestroyBuffer(vulkanDevice->logicalDevice, stagingBuffer, nullptr);

			// Update descriptor image info member that can be used for setting up descriptor sets
			texture->updateDescriptor();
		}

		/**
		* Creates a 2D texture from a buffer
		*
		* @param buffer Buffer containing texture data to upload
		* @param bufferSize Size of the buffer in machine units
		* @param width Width of the texture to create
		* @param hidth Height of the texture to create
		* @param format Vulkan format of the image data stored in the file
		* @param texture Pointer to the texture object to load the image into
		* @param (Optional) filter Texture filtering for the sampler (defaults to VK_FILTER_LINEAR)
		* @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT)
		* @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
		*/
		void createTexture(
			void* buffer, 
			VkDeviceSize bufferSize, 
			VkFormat format, 
			uint32_t width, 
			uint32_t height, 
			VulkanTexture *texture, 
			VkFilter filter = VK_FILTER_LINEAR, 
			VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT,
			VkImageLayout imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
		{
			assert(buffer);

			texture->device = vulkanDevice->logicalDevice;
			texture->width = width;
			texture->height = height;
			texture->mipLevels = 1;

			VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo();
			VkMemoryRequirements memReqs;

			// Use a separate command buffer for texture loading
			VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
			VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo));

			// Create a host-visible staging buffer that contains the raw image data
			VkBuffer stagingBuffer;
			VkDeviceMemory stagingMemory;

			VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo();
			bufferCreateInfo.size = bufferSize;
			// This buffer is used as a transfer source for the buffer copy
			bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
			bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

			VK_CHECK_RESULT(vkCreateBuffer(vulkanDevice->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));

			// Get memory requirements for the staging buffer (alignment, memory type bits)
			vkGetBufferMemoryRequirements(vulkanDevice->logicalDevice, stagingBuffer, &memReqs);

			memAllocInfo.allocationSize = memReqs.size;
			// Get memory type index for a host visible buffer
			memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);

			VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memAllocInfo, nullptr, &stagingMemory));
			VK_CHECK_RESULT(vkBindBufferMemory(vulkanDevice->logicalDevice, stagingBuffer, stagingMemory, 0));

			// Copy texture data into staging buffer
			uint8_t *data;
			VK_CHECK_RESULT(vkMapMemory(vulkanDevice->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void **)&data));
			memcpy(data, buffer, bufferSize);
			vkUnmapMemory(vulkanDevice->logicalDevice, stagingMemory);

			VkBufferImageCopy bufferCopyRegion = {};
			bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
			bufferCopyRegion.imageSubresource.mipLevel = 0;
			bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
			bufferCopyRegion.imageSubresource.layerCount = 1;
			bufferCopyRegion.imageExtent.width = width;
			bufferCopyRegion.imageExtent.height = height;
			bufferCopyRegion.imageExtent.depth = 1;
			bufferCopyRegion.bufferOffset = 0;

			// Create optimal tiled target image
			VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo();
			imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
			imageCreateInfo.format = format;
			imageCreateInfo.mipLevels = texture->mipLevels;
			imageCreateInfo.arrayLayers = 1;
			imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
			imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
			imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
			imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
			imageCreateInfo.extent = { texture->width, texture->height, 1 };
			imageCreateInfo.usage = imageUsageFlags;
			// Ensure that the TRANSFER_DST bit is set for staging
			if (!(imageCreateInfo.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
			{
				imageCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
			}
			VK_CHECK_RESULT(vkCreateImage(vulkanDevice->logicalDevice, &imageCreateInfo, nullptr, &texture->image));

			vkGetImageMemoryRequirements(vulkanDevice->logicalDevice, texture->image, &memReqs);

			memAllocInfo.allocationSize = memReqs.size;

			memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
			VK_CHECK_RESULT(vkAllocateMemory(vulkanDevice->logicalDevice, &memAllocInfo, nullptr, &texture->deviceMemory));
			VK_CHECK_RESULT(vkBindImageMemory(vulkanDevice->logicalDevice, texture->image, texture->deviceMemory, 0));

			VkImageSubresourceRange subresourceRange = {};
			subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
			subresourceRange.baseMipLevel = 0;
			subresourceRange.levelCount = texture->mipLevels;
			subresourceRange.layerCount = 1;

			// Image barrier for optimal image (target)
			// Optimal image will be used as destination for the copy
			setImageLayout(
				cmdBuffer,
				texture->image,
				VK_IMAGE_ASPECT_COLOR_BIT,
				VK_IMAGE_LAYOUT_UNDEFINED,
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				subresourceRange);

			// Copy mip levels from staging buffer
			vkCmdCopyBufferToImage(
				cmdBuffer,
				stagingBuffer,
				texture->image,
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				1,
				&bufferCopyRegion
			);

			// Change texture image layout to shader read after all mip levels have been copied
			texture->imageLayout = imageLayout;
			setImageLayout(
				cmdBuffer,
				texture->image,
				VK_IMAGE_ASPECT_COLOR_BIT,
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
				texture->imageLayout,
				subresourceRange);

			// Submit command buffer containing copy and image layout commands
			VK_CHECK_RESULT(vkEndCommandBuffer(cmdBuffer));

			// Create a fence to make sure that the copies have finished before continuing
			VkFence copyFence;
			VkFenceCreateInfo fenceCreateInfo = vkTools::initializers::fenceCreateInfo(VK_FLAGS_NONE);
			VK_CHECK_RESULT(vkCreateFence(vulkanDevice->logicalDevice, &fenceCreateInfo, nullptr, &copyFence));

			VkSubmitInfo submitInfo = vkTools::initializers::submitInfo();
			submitInfo.commandBufferCount = 1;
			submitInfo.pCommandBuffers = &cmdBuffer;

			VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, copyFence));

			VK_CHECK_RESULT(vkWaitForFences(vulkanDevice->logicalDevice, 1, &copyFence, VK_TRUE, DEFAULT_FENCE_TIMEOUT));

			vkDestroyFence(vulkanDevice->logicalDevice, copyFence, nullptr);

			// Clean up staging resources
			vkFreeMemory(vulkanDevice->logicalDevice, stagingMemory, nullptr);
			vkDestroyBuffer(vulkanDevice->logicalDevice, stagingBuffer, nullptr);

			// Create sampler
			VkSamplerCreateInfo sampler = {};
			sampler.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
			sampler.magFilter = filter;
			sampler.minFilter = filter;
			sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
			sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
			sampler.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
			sampler.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
			sampler.mipLodBias = 0.0f;
			sampler.compareOp = VK_COMPARE_OP_NEVER;
			sampler.minLod = 0.0f;
			sampler.maxLod = 0.0f;
			VK_CHECK_RESULT(vkCreateSampler(vulkanDevice->logicalDevice, &sampler, nullptr, &texture->sampler));

			// Create image view
			VkImageViewCreateInfo view = {};
			view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
			view.pNext = NULL;
			view.viewType = VK_IMAGE_VIEW_TYPE_2D;
			view.format = format;
			view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
			view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
			view.subresourceRange.levelCount = 1;
			view.image = texture->image;
			VK_CHECK_RESULT(vkCreateImageView(vulkanDevice->logicalDevice, &view, nullptr, &texture->view));

			// Update descriptor image info member that can be used for setting up descriptor sets
			texture->updateDescriptor();
		}

		/**
		* Free all Vulkan resources used by a texture object
		*
		* @param texture Texture object whose resources are to be freed
		*/
		void destroyTexture(VulkanTexture texture)
		{
			vkDestroyImageView(vulkanDevice->logicalDevice, texture.view, nullptr);
			vkDestroyImage(vulkanDevice->logicalDevice, texture.image, nullptr);
			vkDestroySampler(vulkanDevice->logicalDevice, texture.sampler, nullptr);
			vkFreeMemory(vulkanDevice->logicalDevice, texture.deviceMemory, nullptr);
		}
	};
};

A src/vulkanbuffer.hpp => src/vulkanbuffer.hpp +161 -0
@@ 0,0 1,161 @@
/*
* Vulkan buffer class
*
* Encapsulates a Vulkan buffer
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/

#pragma once

#include <vector>

#include "vulkan/vulkan.h"
#include "vulkantools.h"

namespace vk
{	
	/**
	* @brief Encapsulates access to a Vulkan buffer backed up by device memory
	* @note To be filled by an external source like the VulkanDevice
	*/
	struct Buffer
	{
		VkBuffer buffer;
		VkDevice device;
		VkDeviceMemory memory;
		VkDescriptorBufferInfo descriptor;
		VkDeviceSize size = 0;
		VkDeviceSize alignment = 0;
		void* mapped = nullptr;

		/** @brief Usage flags to be filled by external source at buffer creation (to query at some later point) */
		VkBufferUsageFlags usageFlags;
		/** @brief Memory propertys flags to be filled by external source at buffer creation (to query at some later point) */
		VkMemoryPropertyFlags memoryPropertyFlags;

		/** 
		* Map a memory range of this buffer. If successful, mapped points to the specified buffer range.
		* 
		* @param size (Optional) Size of the memory range to map. Pass VK_WHOLE_SIZE to map the complete buffer range.
		* @param offset (Optional) Byte offset from beginning
		* 
		* @return VkResult of the buffer mapping call
		*/
		VkResult map(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0)
		{
			return vkMapMemory(device, memory, offset, size, 0, &mapped);
		}

		/**
		* Unmap a mapped memory range
		*
		* @note Does not return a result as vkUnmapMemory can't fail
		*/
		void unmap()
		{
			if (mapped)
			{
				vkUnmapMemory(device, memory);
				mapped = nullptr;
			}
		}

		/** 
		* Attach the allocated memory block to the buffer
		* 
		* @param offset (Optional) Byte offset (from the beginning) for the memory region to bind
		* 
		* @return VkResult of the bindBufferMemory call
		*/
		VkResult bind(VkDeviceSize offset = 0)
		{
			return vkBindBufferMemory(device, buffer, memory, offset);
		}

		/**
		* Setup the default descriptor for this buffer
		*
		* @param size (Optional) Size of the memory range of the descriptor
		* @param offset (Optional) Byte offset from beginning
		*
		*/
		void setupDescriptor(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0)
		{
			descriptor.offset = offset;
			descriptor.buffer = buffer;
			descriptor.range = size;
		}

		/**
		* Copies the specified data to the mapped buffer
		* 
		* @param data Pointer to the data to copy
		* @param size Size of the data to copy in machine units
		*
		*/
		void copyTo(void* data, VkDeviceSize size)
		{
			assert(mapped);
			memcpy(mapped, data, size);
		}

		/** 
		* Flush a memory range of the buffer to make it visible to the device
		*
		* @note Only required for non-coherent memory
		*
		* @param size (Optional) Size of the memory range to flush. Pass VK_WHOLE_SIZE to flush the complete buffer range.
		* @param offset (Optional) Byte offset from beginning
		*
		* @return VkResult of the flush call
		*/
		VkResult flush(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0)
		{
			VkMappedMemoryRange mappedRange = {};
			mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
			mappedRange.memory = memory;
			mappedRange.offset = offset;
			mappedRange.size = size;
			return vkFlushMappedMemoryRanges(device, 1, &mappedRange);
		}

		/**
		* Invalidate a memory range of the buffer to make it visible to the host
		*
		* @note Only required for non-coherent memory
		*
		* @param size (Optional) Size of the memory range to invalidate. Pass VK_WHOLE_SIZE to invalidate the complete buffer range.
		* @param offset (Optional) Byte offset from beginning
		*
		* @return VkResult of the invalidate call
		*/
		VkResult invalidate(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0)
		{
			VkMappedMemoryRange mappedRange = {};
			mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
			mappedRange.memory = memory;
			mappedRange.offset = offset;
			mappedRange.size = size;
			return vkInvalidateMappedMemoryRanges(device, 1, &mappedRange);
		}

		/** 
		* Release all Vulkan resources held by this buffer
		*/
		void destroy()
		{
			if (buffer)
			{
				vkDestroyBuffer(device, buffer, nullptr);
			}
			if (memory)
			{
				vkFreeMemory(device, memory, nullptr);
			}
		}

	};
}
\ No newline at end of file

A src/vulkandebug.cpp => src/vulkandebug.cpp +282 -0
@@ 0,0 1,282 @@
/*
* Vulkan examples debug wrapper
* 
* Appendix for VK_EXT_Debug_Report can be found at https://github.com/KhronosGroup/Vulkan-Docs/blob/1.0-VK_EXT_debug_report/doc/specs/vulkan/appendices/debug_report.txt
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/

#include "vulkandebug.h"
#include <iostream>

namespace vkDebug
{
	int validationLayerCount = 1;
	const char *validationLayerNames[] = 
	{
		// This is a meta layer that enables all of the standard
		// validation layers in the correct order :
		// threading, parameter_validation, device_limits, object_tracker, image, core_validation, swapchain, and unique_objects
		"VK_LAYER_LUNARG_standard_validation"
	};

	PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback = VK_NULL_HANDLE;
	PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback = VK_NULL_HANDLE;
	PFN_vkDebugReportMessageEXT dbgBreakCallback = VK_NULL_HANDLE;

	VkDebugReportCallbackEXT msgCallback;

	VkBool32 messageCallback(
		VkDebugReportFlagsEXT flags,
		VkDebugReportObjectTypeEXT objType,
		uint64_t srcObject,
		size_t location,
		int32_t msgCode,
		const char* pLayerPrefix,
		const char* pMsg,
		void* pUserData)
	{
		// Select prefix depending on flags passed to the callback
		// Note that multiple flags may be set for a single validation message
		std::string prefix("");

		// Error that may result in undefined behaviour
		if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
		{
			prefix += "ERROR:";
		};
		// Warnings may hint at unexpected / non-spec API usage
		if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
		{
			prefix += "WARNING:";
		};
		// May indicate sub-optimal usage of the API
		if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
		{
			prefix += "PERFORMANCE:";
		};
		// Informal messages that may become handy during debugging
		if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT)
		{
			prefix += "INFO:";
		}
		// Diagnostic info from the Vulkan loader and layers
		// Usually not helpful in terms of API usage, but may help to debug layer and loader problems 
		if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT)
		{
			prefix += "DEBUG:";
		}

		// Display message to default output (console if activated)
		std::cout << prefix << " [" << pLayerPrefix << "] Code " << msgCode << " : " << pMsg << "\n";

		fflush(stdout);

		// The return value of this callback controls wether the Vulkan call that caused
		// the validation message will be aborted or not
		// We return VK_FALSE as we DON'T want Vulkan calls that cause a validation message 
		// (and return a VkResult) to abort
		// If you instead want to have calls abort, pass in VK_TRUE and the function will 
		// return VK_ERROR_VALIDATION_FAILED_EXT 
		return VK_FALSE;
	}

	void setupDebugging(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportCallbackEXT callBack)
	{
		CreateDebugReportCallback = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"));
		DestroyDebugReportCallback = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"));
		dbgBreakCallback = reinterpret_cast<PFN_vkDebugReportMessageEXT>(vkGetInstanceProcAddr(instance, "vkDebugReportMessageEXT"));

		VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {};
		dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
		dbgCreateInfo.pfnCallback = (PFN_vkDebugReportCallbackEXT)messageCallback;
		dbgCreateInfo.flags = flags;

		VkResult err = CreateDebugReportCallback(
			instance,
			&dbgCreateInfo,
			nullptr,
			(callBack != VK_NULL_HANDLE) ? &callBack : &msgCallback);
		assert(!err);
	}
	
	void freeDebugCallback(VkInstance instance)
	{
		if (msgCallback != VK_NULL_HANDLE)
		{
			DestroyDebugReportCallback(instance, msgCallback, nullptr);
		}
	}

	namespace DebugMarker
	{
		bool active = false;

		PFN_vkDebugMarkerSetObjectTagEXT pfnDebugMarkerSetObjectTag = VK_NULL_HANDLE;
		PFN_vkDebugMarkerSetObjectNameEXT pfnDebugMarkerSetObjectName = VK_NULL_HANDLE;
		PFN_vkCmdDebugMarkerBeginEXT pfnCmdDebugMarkerBegin = VK_NULL_HANDLE;
		PFN_vkCmdDebugMarkerEndEXT pfnCmdDebugMarkerEnd = VK_NULL_HANDLE;
		PFN_vkCmdDebugMarkerInsertEXT pfnCmdDebugMarkerInsert = VK_NULL_HANDLE;

		void setup(VkDevice device)
		{
			pfnDebugMarkerSetObjectTag = reinterpret_cast<PFN_vkDebugMarkerSetObjectTagEXT>(vkGetDeviceProcAddr(device, "vkDebugMarkerSetObjectTagEXT"));
			pfnDebugMarkerSetObjectName = reinterpret_cast<PFN_vkDebugMarkerSetObjectNameEXT>(vkGetDeviceProcAddr(device, "vkDebugMarkerSetObjectNameEXT"));
			pfnCmdDebugMarkerBegin = reinterpret_cast<PFN_vkCmdDebugMarkerBeginEXT>(vkGetDeviceProcAddr(device, "vkCmdDebugMarkerBeginEXT"));
			pfnCmdDebugMarkerEnd = reinterpret_cast<PFN_vkCmdDebugMarkerEndEXT>(vkGetDeviceProcAddr(device, "vkCmdDebugMarkerEndEXT"));
			pfnCmdDebugMarkerInsert = reinterpret_cast<PFN_vkCmdDebugMarkerInsertEXT>(vkGetDeviceProcAddr(device, "vkCmdDebugMarkerInsertEXT"));

			// Set flag if at least one function pointer is present
			active = (pfnDebugMarkerSetObjectName != VK_NULL_HANDLE);
		}

		void setObjectName(VkDevice device, uint64_t object, VkDebugReportObjectTypeEXT objectType, const char *name)
		{
			// Check for valid function pointer (may not be present if not running in a debugging application)
			if (pfnDebugMarkerSetObjectName)
			{
				VkDebugMarkerObjectNameInfoEXT nameInfo = {};
				nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
				nameInfo.objectType = objectType;
				nameInfo.object = object;
				nameInfo.pObjectName = name;
				pfnDebugMarkerSetObjectName(device, &nameInfo);
			}
		}

		void setObjectTag(VkDevice device, uint64_t object, VkDebugReportObjectTypeEXT objectType, uint64_t name, size_t tagSize, const void* tag)
		{
			// Check for valid function pointer (may not be present if not running in a debugging application)
			if (pfnDebugMarkerSetObjectTag)
			{
				VkDebugMarkerObjectTagInfoEXT tagInfo = {};
				tagInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT;
				tagInfo.objectType = objectType;
				tagInfo.object = object;
				tagInfo.tagName = name;
				tagInfo.tagSize = tagSize;
				tagInfo.pTag = tag;
				pfnDebugMarkerSetObjectTag(device, &tagInfo);
			}
		}

		void beginRegion(VkCommandBuffer cmdbuffer, const char* pMarkerName, glm::vec4 color)
		{
			// Check for valid function pointer (may not be present if not running in a debugging application)
			if (pfnCmdDebugMarkerBegin)
			{
				VkDebugMarkerMarkerInfoEXT markerInfo = {};
				markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
				memcpy(markerInfo.color, &color[0], sizeof(float) * 4);
				markerInfo.pMarkerName = pMarkerName;
				pfnCmdDebugMarkerBegin(cmdbuffer, &markerInfo);
			}
		}

		void insert(VkCommandBuffer cmdbuffer, std::string markerName, glm::vec4 color)
		{
			// Check for valid function pointer (may not be present if not running in a debugging application)
			if (pfnCmdDebugMarkerInsert)
			{
				VkDebugMarkerMarkerInfoEXT markerInfo = {};
				markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
				memcpy(markerInfo.color, &color[0], sizeof(float) * 4);
				markerInfo.pMarkerName = markerName.c_str();
				pfnCmdDebugMarkerInsert(cmdbuffer, &markerInfo);
			}
		}

		void endRegion(VkCommandBuffer cmdBuffer)
		{
			// Check for valid function (may not be present if not runnin in a debugging application)
			if (pfnCmdDebugMarkerEnd)
			{
				pfnCmdDebugMarkerEnd(cmdBuffer);
			}
		}

		void setCommandBufferName(VkDevice device, VkCommandBuffer cmdBuffer, const char * name)
		{
			setObjectName(device, (uint64_t)cmdBuffer, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT, name);
		}

		void setQueueName(VkDevice device, VkQueue queue, const char * name)
		{
			setObjectName(device, (uint64_t)queue, VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT, name);
		}

		void setImageName(VkDevice device, VkImage image, const char * name)
		{
			setObjectName(device, (uint64_t)image, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, name);
		}

		void setSamplerName(VkDevice device, VkSampler sampler, const char * name)
		{
			setObjectName(device, (uint64_t)sampler, VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT, name);
		}

		void setBufferName(VkDevice device, VkBuffer buffer, const char * name)
		{
			setObjectName(device, (uint64_t)buffer, VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, name);
		}

		void setDeviceMemoryName(VkDevice device, VkDeviceMemory memory, const char * name)
		{
			setObjectName(device, (uint64_t)memory, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT, name);
		}

		void setShaderModuleName(VkDevice device, VkShaderModule shaderModule, const char * name)
		{
			setObjectName(device, (uint64_t)shaderModule, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT, name);
		}

		void setPipelineName(VkDevice device, VkPipeline pipeline, const char * name)
		{
			setObjectName(device, (uint64_t)pipeline, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, name);
		}

		void setPipelineLayoutName(VkDevice device, VkPipelineLayout pipelineLayout, const char * name)
		{
			setObjectName(device, (uint64_t)pipelineLayout, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT, name);
		}

		void setRenderPassName(VkDevice device, VkRenderPass renderPass, const char * name)
		{
			setObjectName(device, (uint64_t)renderPass, VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT, name);
		}

		void setFramebufferName(VkDevice device, VkFramebuffer framebuffer, const char * name)
		{
			setObjectName(device, (uint64_t)framebuffer, VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT, name);
		}

		void setDescriptorSetLayoutName(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const char * name)
		{
			setObjectName(device, (uint64_t)descriptorSetLayout, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT, name);
		}

		void setDescriptorSetName(VkDevice device, VkDescriptorSet descriptorSet, const char * name)
		{
			setObjectName(device, (uint64_t)descriptorSet, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT, name);
		}

		void setSemaphoreName(VkDevice device, VkSemaphore semaphore, const char * name)
		{
			setObjectName(device, (uint64_t)semaphore, VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT, name);
		}

		void setFenceName(VkDevice device, VkFence fence, const char * name)
		{
			setObjectName(device, (uint64_t)fence, VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT, name);
		}

		void setEventName(VkDevice device, VkEvent _event, const char * name)
		{
			setObjectName(device, (uint64_t)_event, VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT, name);
		}
	};

}


A src/vulkandebug.h => src/vulkandebug.h +99 -0
@@ 0,0 1,99 @@
#pragma once
#include "vulkan/vulkan.h"

#include <math.h>
#include <stdlib.h>
#include <string>
#include <cstring>
#include <fstream>
#include <assert.h>
#include <stdio.h>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#endif
#ifdef __ANDROID__
#include "vulkanandroid.h"
#endif
#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>

namespace vkDebug
{
	// Default validation layers
	extern int validationLayerCount;
	extern const char *validationLayerNames[];

	// Default debug callback
	VkBool32 messageCallback(
		VkDebugReportFlagsEXT flags,
		VkDebugReportObjectTypeEXT objType,
		uint64_t srcObject,
		size_t location,
		int32_t msgCode,
		const char* pLayerPrefix,
		const char* pMsg,
		void* pUserData);

	// Load debug function pointers and set debug callback
	// if callBack is NULL, default message callback will be used
	void setupDebugging(
		VkInstance instance, 
		VkDebugReportFlagsEXT flags, 
		VkDebugReportCallbackEXT callBack);
	// Clear debug callback
	void freeDebugCallback(VkInstance instance);

	// Setup and functions for the VK_EXT_debug_marker_extension
	// Extension spec can be found at https://github.com/KhronosGroup/Vulkan-Docs/blob/1.0-VK_EXT_debug_marker/doc/specs/vulkan/appendices/VK_EXT_debug_marker.txt
	// Note that the extension will only be present if run from an offline debugging application
	// The actual check for extension presence and enabling it on the device is done in the example base class
	// See VulkanExampleBase::createInstance and VulkanExampleBase::createDevice (base/vulkanexamplebase.cpp)
	namespace DebugMarker
	{
		// Set to true if function pointer for the debug marker are available
		extern bool active;

		// Get function pointers for the debug report extensions from the device
		void setup(VkDevice device);

		// Sets the debug name of an object
		// All Objects in Vulkan are represented by their 64-bit handles which are passed into this function
		// along with the object type
		void setObjectName(VkDevice device, uint64_t object, VkDebugReportObjectTypeEXT objectType, const char *name);

		// Set the tag for an object
		void setObjectTag(VkDevice device, uint64_t object, VkDebugReportObjectTypeEXT objectType, uint64_t name, size_t tagSize, const void* tag);

		// Start a new debug marker region
		void beginRegion(VkCommandBuffer cmdbuffer, const char* pMarkerName, glm::vec4 color);

		// Insert a new debug marker into the command buffer
		void insert(VkCommandBuffer cmdbuffer, std::string markerName, glm::vec4 color);

		// End the current debug marker region
		void endRegion(VkCommandBuffer cmdBuffer);

		// Object specific naming functions
		void setCommandBufferName(VkDevice device, VkCommandBuffer cmdBuffer, const char * name);
		void setQueueName(VkDevice device, VkQueue queue, const char * name);
		void setImageName(VkDevice device, VkImage image, const char * name);
		void setSamplerName(VkDevice device, VkSampler sampler, const char * name);
		void setBufferName(VkDevice device, VkBuffer buffer, const char * name);
		void setDeviceMemoryName(VkDevice device, VkDeviceMemory memory, const char * name);
		void setShaderModuleName(VkDevice device, VkShaderModule shaderModule, const char * name);
		void setPipelineName(VkDevice device, VkPipeline pipeline, const char * name);
		void setPipelineLayoutName(VkDevice device, VkPipelineLayout pipelineLayout, const char * name);
		void setRenderPassName(VkDevice device, VkRenderPass renderPass, const char * name);
		void setFramebufferName(VkDevice device, VkFramebuffer framebuffer, const char * name);
		void setDescriptorSetLayoutName(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const char * name);
		void setDescriptorSetName(VkDevice device, VkDescriptorSet descriptorSet, const char * name);
		void setSemaphoreName(VkDevice device, VkSemaphore semaphore, const char * name);
		void setFenceName(VkDevice device, VkFence fence, const char * name);
		void setEventName(VkDevice device, VkEvent _event, const char * name);
	};

}

A src/vulkandevice.hpp => src/vulkandevice.hpp +555 -0
@@ 0,0 1,555 @@
/*
* Vulkan device class
*
* Encapsulates a physical Vulkan device and it's logical representation
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/

#pragma once

#include <exception>
#include <assert.h>
#include <algorithm>
#include "vulkan/vulkan.h"
#include "vulkantools.h"
#include "vulkanbuffer.hpp"

namespace vk
{	
	struct VulkanDevice
	{
		/** @brief Physical device representation */
		VkPhysicalDevice physicalDevice;
		/** @brief Logical device representation (application's view of the device) */
		VkDevice logicalDevice;
		/** @brief Properties of the physical device including limits that the application can check against */
		VkPhysicalDeviceProperties properties;
		/** @brief Features of the physical device that an application can use to check if a feature is supported */
		VkPhysicalDeviceFeatures features;
		/** @brief Memory types and heaps of the physical device */
		VkPhysicalDeviceMemoryProperties memoryProperties;
		/** @brief Queue family properties of the physical device */
		std::vector<VkQueueFamilyProperties> queueFamilyProperties;
		/** @brief List of extensions supported by the device */
		std::vector<std::string> supportedExtensions;

		/** @brief Default command pool for the graphics queue family index */
		VkCommandPool commandPool = VK_NULL_HANDLE;

		/** @brief Set to true when the debug marker extension is detected */
		bool enableDebugMarkers = false;

		/** @brief Contains queue family indices */
		struct
		{
			uint32_t graphics;
			uint32_t compute;
			uint32_t transfer;
		} queueFamilyIndices;

		/**
		* Default constructor
		*
		* @param physicalDevice Physical device that is to be used
		*/
		VulkanDevice(VkPhysicalDevice physicalDevice)
		{
			assert(physicalDevice);
			this->physicalDevice = physicalDevice;

			// Store Properties features, limits and properties of the physical device for later use
			// Device properties also contain limits and sparse properties
			vkGetPhysicalDeviceProperties(physicalDevice, &properties);
			// Features should be checked by the examples before using them
			vkGetPhysicalDeviceFeatures(physicalDevice, &features);
			// Memory properties are used regularly for creating all kinds of buffers
			vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
			// Queue family properties, used for setting up requested queues upon device creation
			uint32_t queueFamilyCount;
			vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
			assert(queueFamilyCount > 0);
			queueFamilyProperties.resize(queueFamilyCount);
			vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data());

			// Get list of supported extensions
			uint32_t extCount = 0;
			vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extCount, nullptr);
			if (extCount > 0)
			{
				std::vector<VkExtensionProperties> extensions(extCount);
				if (vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extCount, &extensions.front()) == VK_SUCCESS)
				{
					for (auto ext : extensions)
					{
						supportedExtensions.push_back(ext.extensionName);
					}
				}
			}
		}

		/** 
		* Default destructor
		*
		* @note Frees the logical device
		*/
		~VulkanDevice()
		{
			if (commandPool)
			{
				vkDestroyCommandPool(logicalDevice, commandPool, nullptr);
			}
			if (logicalDevice)
			{
				vkDestroyDevice(logicalDevice, nullptr);
			}
		}

		/**
		* Get the index of a memory type that has all the requested property bits set
		*
		* @param typeBits Bitmask with bits set for each memory type supported by the resource to request for (from VkMemoryRequirements)
		* @param properties Bitmask of properties for the memory type to request
		* @param (Optional) memTypeFound Pointer to a bool that is set to true if a matching memory type has been found
		* 
		* @return Index of the requested memory type
		*
		* @throw Throws an exception if memTypeFound is null and no memory type could be found that supports the requested properties
		*/
		uint32_t getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32 *memTypeFound = nullptr)
		{
			for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++)
			{
				if ((typeBits & 1) == 1)
				{
					if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
					{
						if (memTypeFound)
						{
							*memTypeFound = true;
						}
						return i;
					}
				}
				typeBits >>= 1;
			}

#if defined(__ANDROID__)
			//todo : Exceptions are disabled by default on Android (need to add LOCAL_CPP_FEATURES += exceptions to Android.mk), so for now just return zero
			if (memTypeFound)
			{
				*memTypeFound = false;
			}
			return 0;
#else
			if (memTypeFound)
			{
				*memTypeFound = false;
				return 0;
			}
			else
			{
				throw std::runtime_error("Could not find a matching memory type");
			}
#endif
		}

		/**
		* Get the index of a queue family that supports the requested queue flags
		*
		* @param queueFlags Queue flags to find a queue family index for
		*
		* @return Index of the queue family index that matches the flags
		*
		* @throw Throws an exception if no queue family index could be found that supports the requested flags
		*/
		uint32_t getQueueFamilyIndex(VkQueueFlagBits queueFlags)
		{
			// Dedicated queue for compute
			// Try to find a queue family index that supports compute but not graphics
			if (queueFlags & VK_QUEUE_COMPUTE_BIT)
			{
				for (uint32_t i = 0; i < static_cast<uint32_t>(queueFamilyProperties.size()); i++)
				{
					if ((queueFamilyProperties[i].queueFlags & queueFlags) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0))
					{
						return i;
						break;
					}
				}
			}

			// Dedicated queue for transfer
			// Try to find a queue family index that supports transfer but not graphics and compute
			if (queueFlags & VK_QUEUE_TRANSFER_BIT)
			{
				for (uint32_t i = 0; i < static_cast<uint32_t>(queueFamilyProperties.size()); i++)
				{
					if ((queueFamilyProperties[i].queueFlags & queueFlags) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) == 0))
					{
						return i;
						break;
					}
				}
			}

			// For other queue types or if no separate compute queue is present, return the first one to support the requested flags
			for (uint32_t i = 0; i < static_cast<uint32_t>(queueFamilyProperties.size()); i++)
			{
				if (queueFamilyProperties[i].queueFlags & queueFlags)
				{
					return i;
					break;
				}
			}

#if defined(__ANDROID__)
			//todo : Exceptions are disabled by default on Android (need to add LOCAL_CPP_FEATURES += exceptions to Android.mk), so for now just return zero
			return 0;
#else
			throw std::runtime_error("Could not find a matching queue family index");
#endif
		}

		/**
		* Create the logical device based on the assigned physical device, also gets default queue family indices
		*
		* @param enabledFeatures Can be used to enable certain features upon device creation
		* @param useSwapChain Set to false for headless rendering to omit the swapchain device extensions
		* @param requestedQueueTypes Bit flags specifying the queue types to be requested from the device  
		*
		* @return VkResult of the device creation call
		*/
		VkResult createLogicalDevice(VkPhysicalDeviceFeatures enabledFeatures, bool useSwapChain = true, VkQueueFlags requestedQueueTypes = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT)
		{			
			// Desired queues need to be requested upon logical device creation
			// Due to differing queue family configurations of Vulkan implementations this can be a bit tricky, especially if the application
			// requests different queue types

			std::vector<VkDeviceQueueCreateInfo> queueCreateInfos{};

			// Get queue family indices for the requested queue family types
			// Note that the indices may overlap depending on the implementation

			const float defaultQueuePriority(0.0f);

			// Graphics queue
			if (requestedQueueTypes & VK_QUEUE_GRAPHICS_BIT)
			{
				queueFamilyIndices.graphics = getQueueFamilyIndex(VK_QUEUE_GRAPHICS_BIT);
				VkDeviceQueueCreateInfo queueInfo{};
				queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
				queueInfo.queueFamilyIndex = queueFamilyIndices.graphics;
				queueInfo.queueCount = 1;
				queueInfo.pQueuePriorities = &defaultQueuePriority;
				queueCreateInfos.push_back(queueInfo);
			}
			else
			{
				queueFamilyIndices.graphics = VK_NULL_HANDLE;
			}

			// Dedicated compute queue
			if (requestedQueueTypes & VK_QUEUE_COMPUTE_BIT)
			{
				queueFamilyIndices.compute = getQueueFamilyIndex(VK_QUEUE_COMPUTE_BIT);
				if (queueFamilyIndices.compute != queueFamilyIndices.graphics)
				{
					// If compute family index differs, we need an additional queue create info for the compute queue
					VkDeviceQueueCreateInfo queueInfo{};
					queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
					queueInfo.queueFamilyIndex = queueFamilyIndices.compute;
					queueInfo.queueCount = 1;
					queueInfo.pQueuePriorities = &defaultQueuePriority;
					queueCreateInfos.push_back(queueInfo);
				}
			}
			else
			{
				// Else we use the same queue
				queueFamilyIndices.compute = queueFamilyIndices.graphics;
			}

			// Dedicated transfer queue
			if (requestedQueueTypes & VK_QUEUE_TRANSFER_BIT)
			{
				queueFamilyIndices.transfer = getQueueFamilyIndex(VK_QUEUE_TRANSFER_BIT);
				if ((queueFamilyIndices.transfer != queueFamilyIndices.graphics) && (queueFamilyIndices.transfer != queueFamilyIndices.compute))
				{
					// If compute family index differs, we need an additional queue create info for the compute queue
					VkDeviceQueueCreateInfo queueInfo{};
					queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
					queueInfo.queueFamilyIndex = queueFamilyIndices.transfer;
					queueInfo.queueCount = 1;
					queueInfo.pQueuePriorities = &defaultQueuePriority;
					queueCreateInfos.push_back(queueInfo);
				}
			}
			else
			{
				// Else we use the same queue
				queueFamilyIndices.transfer = queueFamilyIndices.graphics;
			}

			// Create the logical device representation
			std::vector<const char*> deviceExtensions;
			if (useSwapChain)
			{
				// If the device will be used for presenting to a display via a swapchain we need to request the swapchain extension
				deviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
			}

			VkDeviceCreateInfo deviceCreateInfo = {};
			deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
			deviceCreateInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());;
			deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data();
			deviceCreateInfo.pEnabledFeatures = &enabledFeatures;

			// Enable the debug marker extension if it is present (likely meaning a debugging tool is present)
			if (extensionSupported(VK_EXT_DEBUG_MARKER_EXTENSION_NAME))
			{
				deviceExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
				enableDebugMarkers = true;
			}

			if (deviceExtensions.size() > 0)
			{
				deviceCreateInfo.enabledExtensionCount = (uint32_t)deviceExtensions.size();
				deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data();
			}

			VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &logicalDevice);

			if (result == VK_SUCCESS)
			{
				// Create a default command pool for graphics command buffers
				commandPool = createCommandPool(queueFamilyIndices.graphics);
			}

			return result;
		}

		/**
		* Create a buffer on the device
		*
		* @param usageFlags Usage flag bitmask for the buffer (i.e. index, vertex, uniform buffer)
		* @param memoryPropertyFlags Memory properties for this buffer (i.e. device local, host visible, coherent)
		* @param size Size of the buffer in byes
		* @param buffer Pointer to the buffer handle acquired by the function
		* @param memory Pointer to the memory handle acquired by the function
		* @param data Pointer to the data that should be copied to the buffer after creation (optional, if not set, no data is copied over)
		*
		* @return VK_SUCCESS if buffer handle and memory have been created and (optionally passed) data has been copied
		*/
		VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer *buffer, VkDeviceMemory *memory, void *data = nullptr)
		{
			// Create the buffer handle
			VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(usageFlags, size);
			bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
			VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, buffer));

			// Create the memory backing up the buffer handle
			VkMemoryRequirements memReqs;
			VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo();
			vkGetBufferMemoryRequirements(logicalDevice, *buffer, &memReqs);
			memAlloc.allocationSize = memReqs.size;
			// Find a memory type index that fits the properties of the buffer
			memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags);
			VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAlloc, nullptr, memory));
			
			// If a pointer to the buffer data has been passed, map the buffer and copy over the data
			if (data != nullptr)
			{
				void *mapped;
				VK_CHECK_RESULT(vkMapMemory(logicalDevice, *memory, 0, size, 0, &mapped));
				memcpy(mapped, data, size);
				vkUnmapMemory(logicalDevice, *memory);
			}

			// Attach the memory to the buffer object
			VK_CHECK_RESULT(vkBindBufferMemory(logicalDevice, *buffer, *memory, 0));

			return VK_SUCCESS;
		}

		/**
		* Create a buffer on the device
		*
		* @param usageFlags Usage flag bitmask for the buffer (i.e. index, vertex, uniform buffer)
		* @param memoryPropertyFlags Memory properties for this buffer (i.e. device local, host visible, coherent)
		* @param buffer Pointer to a vk::Vulkan buffer object
		* @param size Size of the buffer in byes
		* @param data Pointer to the data that should be copied to the buffer after creation (optional, if not set, no data is copied over)
		*
		* @return VK_SUCCESS if buffer handle and memory have been created and (optionally passed) data has been copied
		*/
		VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, vk::Buffer *buffer, VkDeviceSize size, void *data = nullptr)
		{
			buffer->device = logicalDevice;

			// Create the buffer handle
			VkBufferCreateInfo bufferCreateInfo = vkTools::initializers::bufferCreateInfo(usageFlags, size);
			VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, &buffer->buffer));

			// Create the memory backing up the buffer handle
			VkMemoryRequirements memReqs;
			VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo();
			vkGetBufferMemoryRequirements(logicalDevice, buffer->buffer, &memReqs);
			memAlloc.allocationSize = memReqs.size;
			// Find a memory type index that fits the properties of the buffer
			memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags);
			VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAlloc, nullptr, &buffer->memory));

			buffer->alignment = memReqs.alignment;
			buffer->size = memAlloc.allocationSize;
			buffer->usageFlags = usageFlags;
			buffer->memoryPropertyFlags = memoryPropertyFlags;

			// If a pointer to the buffer data has been passed, map the buffer and copy over the data
			if (data != nullptr)
			{
				VK_CHECK_RESULT(buffer->map());
				memcpy(buffer->mapped, data, size);
				buffer->unmap();
			}

			// Initialize a default descriptor that covers the whole buffer size
			buffer->setupDescriptor();

			// Attach the memory to the buffer object
			return buffer->bind();
		}

		/**
		* Copy buffer data from src to dst using VkCmdCopyBuffer
		* 
		* @param src Pointer to the source buffer to copy from
		* @param dst Pointer to the destination buffer to copy tp
		* @param queue Pointer
		* @param copyRegion (Optional) Pointer to a copy region, if NULL, the whole buffer is copied
		*
		* @note Source and destionation pointers must have the approriate transfer usage flags set (TRANSFER_SRC / TRANSFER_DST)
		*/
		void copyBuffer(vk::Buffer *src, vk::Buffer *dst, VkQueue queue, VkBufferCopy *copyRegion = nullptr)
		{
			assert(dst->size <= src->size);
			assert(src->buffer && src->buffer);
			VkCommandBuffer copyCmd = createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
			VkBufferCopy bufferCopy{};
			if (copyRegion == nullptr)
			{
				bufferCopy.size = src->size;
			}
			else
			{
				bufferCopy = *copyRegion;
			}

			vkCmdCopyBuffer(copyCmd, src->buffer, dst->buffer, 1, &bufferCopy);

			flushCommandBuffer(copyCmd, queue);
		}

		/** 
		* Create a command pool for allocation command buffers from
		* 
		* @param queueFamilyIndex Family index of the queue to create the command pool for
		* @param createFlags (Optional) Command pool creation flags (Defaults to VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT)
		*
		* @note Command buffers allocated from the created pool can only be submitted to a queue with the same family index
		*
		* @return A handle to the created command buffer
		*/
		VkCommandPool createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT)
		{
			VkCommandPoolCreateInfo cmdPoolInfo = {};
			cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
			cmdPoolInfo.queueFamilyIndex = queueFamilyIndex;
			cmdPoolInfo.flags = createFlags;
			VkCommandPool cmdPool;
			VK_CHECK_RESULT(vkCreateCommandPool(logicalDevice, &cmdPoolInfo, nullptr, &cmdPool));
			return cmdPool;
		}

		/**
		* Allocate a command buffer from the command pool
		*
		* @param level Level of the new command buffer (primary or secondary)
		* @param (Optional) begin If true, recording on the new command buffer will be started (vkBeginCommandBuffer) (Defaults to false)
		*
		* @return A handle to the allocated command buffer
		*/
		VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, bool begin = false)
		{
			VkCommandBufferAllocateInfo cmdBufAllocateInfo = vkTools::initializers::commandBufferAllocateInfo(commandPool, level, 1);

			VkCommandBuffer cmdBuffer;
			VK_CHECK_RESULT(vkAllocateCommandBuffers(logicalDevice, &cmdBufAllocateInfo, &cmdBuffer));

			// If requested, also start recording for the new command buffer
			if (begin)
			{
				VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
				VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo));
			}

			return cmdBuffer;
		}

		/**
		* Finish command buffer recording and submit it to a queue
		*
		* @param commandBuffer Command buffer to flush
		* @param queue Queue to submit the command buffer to 
		* @param free (Optional) Free the command buffer once it has been submitted (Defaults to true)
		*
		* @note The queue that the command buffer is submitted to must be from the same family index as the pool it was allocated from
		* @note Uses a fence to ensure command buffer has finished executing
		*/
		void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true)
		{
			if (commandBuffer == VK_NULL_HANDLE)
			{
				return;
			}

			VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer));

			VkSubmitInfo submitInfo = vkTools::initializers::submitInfo();
			submitInfo.commandBufferCount = 1;
			submitInfo.pCommandBuffers = &commandBuffer;

			// Create fence to ensure that the command buffer has finished executing
			VkFenceCreateInfo fenceInfo = vkTools::initializers::fenceCreateInfo(VK_FLAGS_NONE);
			VkFence fence;
			VK_CHECK_RESULT(vkCreateFence(logicalDevice, &fenceInfo, nullptr, &fence));
			
			// Submit to the queue
			VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence));
			// Wait for the fence to signal that command buffer has finished executing
			VK_CHECK_RESULT(vkWaitForFences(logicalDevice, 1, &fence, VK_TRUE, DEFAULT_FENCE_TIMEOUT));

			vkDestroyFence(logicalDevice, fence, nullptr);

			if (free)
			{
				vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer);
			}
		}

		/**
		* Check if an extension is supported by the (physical device)
		*
		* @param extension Name of the extension to check
		*
		* @return True if the extension is supported (present in the list read at device creation time)
		*/
		bool extensionSupported(std::string extension)
		{
			return (std::find(supportedExtensions.begin(), supportedExtensions.end(), extension) != supportedExtensions.end());
		}

	};
}

A src/vulkanexamplebase.cpp => src/vulkanexamplebase.cpp +2046 -0
@@ 0,0 1,2046 @@
/*
* Vulkan Example base class
*
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/

#include <vulkan/vulkan.h>

#include "vulkanexamplebase.h"

std::vector<const char*> VulkanExampleBase::args;

VkResult VulkanExampleBase::createInstance(bool enableValidation)
{
	this->settings.validation = enableValidation;

	VkApplicationInfo appInfo = {};
	appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
	appInfo.pApplicationName = name.c_str();
	appInfo.pEngineName = name.c_str();
	appInfo.apiVersion = VK_API_VERSION_1_0;

	std::vector<const char*> enabledExtensions = { VK_KHR_SURFACE_EXTENSION_NAME };

	// Enable surface extensions depending on os
#if defined(_WIN32)
	enabledExtensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#elif defined(__ANDROID__)
	enabledExtensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
#elif defined(_DIRECT2DISPLAY)
	enabledExtensions.push_back(VK_KHR_DISPLAY_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
	enabledExtensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
#elif defined(__linux__)
	enabledExtensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
#endif

	VkInstanceCreateInfo instanceCreateInfo = {};
	instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
	instanceCreateInfo.pNext = NULL;
	instanceCreateInfo.pApplicationInfo = &appInfo;
	if (enabledExtensions.size() > 0)
	{
		if (settings.validation)
		{
			enabledExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
		}
		instanceCreateInfo.enabledExtensionCount = (uint32_t)enabledExtensions.size();
		instanceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data();
	}
	if (settings.validation)
	{
		instanceCreateInfo.enabledLayerCount = vkDebug::validationLayerCount;
		instanceCreateInfo.ppEnabledLayerNames = vkDebug::validationLayerNames;
	}
	return vkCreateInstance(&instanceCreateInfo, nullptr, &instance);
}

std::string VulkanExampleBase::getWindowTitle()
{
	std::string device(deviceProperties.deviceName);
	std::string windowTitle;
	windowTitle = title + " - " + device;
	if (!enableTextOverlay)
	{
		windowTitle += " - " + std::to_string(frameCounter) + " fps";
	}
	return windowTitle;
}

const std::string VulkanExampleBase::getAssetPath()
{
#if defined(__ANDROID__)
	return "";
#else
	return "./../data/";
#endif
}

bool VulkanExampleBase::checkCommandBuffers()
{
	for (auto& cmdBuffer : drawCmdBuffers)
	{
		if (cmdBuffer == VK_NULL_HANDLE)
		{
			return false;
		}
	}
	return true;
}

void VulkanExampleBase::createCommandBuffers()
{
	// Create one command buffer for each swap chain image and reuse for rendering
	drawCmdBuffers.resize(swapChain.imageCount);

	VkCommandBufferAllocateInfo cmdBufAllocateInfo =
		vkTools::initializers::commandBufferAllocateInfo(
			cmdPool,
			VK_COMMAND_BUFFER_LEVEL_PRIMARY,
			static_cast<uint32_t>(drawCmdBuffers.size()));

	VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, drawCmdBuffers.data()));
}

void VulkanExampleBase::destroyCommandBuffers()
{
	vkFreeCommandBuffers(device, cmdPool, static_cast<uint32_t>(drawCmdBuffers.size()), drawCmdBuffers.data());
}

VkCommandBuffer VulkanExampleBase::createCommandBuffer(VkCommandBufferLevel level, bool begin)
{
	VkCommandBuffer cmdBuffer;

	VkCommandBufferAllocateInfo cmdBufAllocateInfo =
		vkTools::initializers::commandBufferAllocateInfo(
			cmdPool,
			level,
			1);

	VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, &cmdBuffer));

	// If requested, also start the new command buffer
	if (begin)
	{
		VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo();
		VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffer, &cmdBufInfo));
	}

	return cmdBuffer;
}

void VulkanExampleBase::flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free)
{
	if (commandBuffer == VK_NULL_HANDLE)
	{
		return;
	}
	
	VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer));

	VkSubmitInfo submitInfo = {};
	submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
	submitInfo.commandBufferCount = 1;
	submitInfo.pCommandBuffers = &commandBuffer;

	VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
	VK_CHECK_RESULT(vkQueueWaitIdle(queue));

	if (free)
	{
		vkFreeCommandBuffers(device, cmdPool, 1, &commandBuffer);
	}
}

void VulkanExampleBase::createPipelineCache()
{
	VkPipelineCacheCreateInfo pipelineCacheCreateInfo = {};
	pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
	VK_CHECK_RESULT(vkCreatePipelineCache(device, &pipelineCacheCreateInfo, nullptr, &pipelineCache));
}

void VulkanExampleBase::prepare()
{
	if (vulkanDevice->enableDebugMarkers)
	{
		vkDebug::DebugMarker::setup(device);
	}
	createCommandPool();
	setupSwapChain();
	createCommandBuffers();
	setupDepthStencil();
	setupRenderPass();
	createPipelineCache();
	setupFrameBuffer();
	// Create a simple texture loader class
	textureLoader = new vkTools::VulkanTextureLoader(vulkanDevice, queue, cmdPool);
	if (enableTextOverlay)
	{
		// Load the text rendering shaders
		std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
		shaderStages.push_back(loadShader(getAssetPath() + "shaders/base/textoverlay.vert.spv", VK_SHADER_STAGE_VERTEX_BIT));
		shaderStages.push_back(loadShader(getAssetPath() + "shaders/base/textoverlay.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT));
		textOverlay = new VulkanTextOverlay(
			vulkanDevice,
			queue,
			frameBuffers,
			swapChain.colorFormat,
			depthFormat,
			&width,
			&height,
			shaderStages
			);
		updateTextOverlay();
	}
}

VkPipelineShaderStageCreateInfo VulkanExampleBase::loadShader(std::string fileName, VkShaderStageFlagBits stage)
{
	VkPipelineShaderStageCreateInfo shaderStage = {};
	shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
	shaderStage.stage = stage;
#if defined(__ANDROID__)
	shaderStage.module = vkTools::loadShader(androidApp->activity->assetManager, fileName.c_str(), device, stage);
#else
	shaderStage.module = vkTools::loadShader(fileName.c_str(), device, stage);
#endif
	shaderStage.pName = "main"; // todo : make param