<NME> renderer_vk.cpp <BEF> /* * Copyright 2011-2022 Branimir Karadzic. All rights reserved. * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE */ #include "bgfx_p.h" #if BGFX_CONFIG_RENDERER_VULKAN # include <bx/pixelformat.h> # include "renderer_vk.h" # include "shader_spirv.h" #if BX_PLATFORM_OSX # import <Cocoa/Cocoa.h> # import <Foundation/Foundation.h> # import <QuartzCore/QuartzCore.h> # import <Metal/Metal.h> #endif // BX_PLATFORM_OSX namespace bgfx { namespace vk { static char s_viewName[BGFX_CONFIG_MAX_VIEWS][BGFX_CONFIG_MAX_VIEW_NAME]; inline void setViewType(ViewId _view, const bx::StringView _str) { if (BX_ENABLED(BGFX_CONFIG_DEBUG_ANNOTATION || BGFX_CONFIG_PROFILER) ) { bx::memCopy(&s_viewName[_view][3], _str.getPtr(), _str.getLength() ); } } struct PrimInfo { VkPrimitiveTopology m_topology; uint32_t m_min; uint32_t m_div; uint32_t m_sub; }; static const PrimInfo s_primInfo[] = { { VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 3, 3, 0 }, { VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 3, 1, 2 }, { VK_PRIMITIVE_TOPOLOGY_LINE_LIST, 2, 2, 0 }, { VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, 2, 1, 1 }, { VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 1, 1, 0 }, { VK_PRIMITIVE_TOPOLOGY_MAX_ENUM, 0, 0, 0 }, }; BX_STATIC_ASSERT(Topology::Count == BX_COUNTOF(s_primInfo)-1); static MsaaSamplerVK s_msaa[] = { { 1, VK_SAMPLE_COUNT_1_BIT }, { 2, VK_SAMPLE_COUNT_2_BIT }, { 4, VK_SAMPLE_COUNT_4_BIT }, { 8, VK_SAMPLE_COUNT_8_BIT }, { 16, VK_SAMPLE_COUNT_16_BIT }, }; static const VkBlendFactor s_blendFactor[][2] = { { VkBlendFactor(0), VkBlendFactor(0) }, // ignored { VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_ZERO }, // ZERO { VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ONE }, // ONE { VK_BLEND_FACTOR_SRC_COLOR, VK_BLEND_FACTOR_SRC_ALPHA }, // SRC_COLOR { VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA }, // INV_SRC_COLOR { VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_SRC_ALPHA }, // SRC_ALPHA { VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA }, // INV_SRC_ALPHA { VK_BLEND_FACTOR_DST_ALPHA, VK_BLEND_FACTOR_DST_ALPHA }, // DST_ALPHA { VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA }, // INV_DST_ALPHA { VK_BLEND_FACTOR_DST_COLOR, VK_BLEND_FACTOR_DST_ALPHA }, // DST_COLOR { VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA }, // INV_DST_COLOR { VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE }, // SRC_ALPHA_SAT { VK_BLEND_FACTOR_CONSTANT_COLOR, VK_BLEND_FACTOR_CONSTANT_COLOR }, // FACTOR { VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR }, // INV_FACTOR }; static const VkBlendOp s_blendEquation[] = { VK_BLEND_OP_ADD, VK_BLEND_OP_SUBTRACT, VK_BLEND_OP_REVERSE_SUBTRACT, VK_BLEND_OP_MIN, VK_BLEND_OP_MAX, }; static const VkCompareOp s_cmpFunc[] = { VkCompareOp(0), // ignored VK_COMPARE_OP_LESS, VK_COMPARE_OP_LESS_OR_EQUAL, VK_COMPARE_OP_EQUAL, VK_COMPARE_OP_GREATER_OR_EQUAL, VK_COMPARE_OP_GREATER, VK_COMPARE_OP_NOT_EQUAL, VK_COMPARE_OP_NEVER, VK_COMPARE_OP_ALWAYS, }; static const VkStencilOp s_stencilOp[] = { VK_STENCIL_OP_ZERO, VK_STENCIL_OP_KEEP, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_INCREMENT_AND_WRAP, VK_STENCIL_OP_INCREMENT_AND_CLAMP, VK_STENCIL_OP_DECREMENT_AND_WRAP, VK_STENCIL_OP_DECREMENT_AND_CLAMP, VK_STENCIL_OP_INVERT, }; static const VkCullModeFlagBits s_cullMode[] = { VK_CULL_MODE_NONE, VK_CULL_MODE_FRONT_BIT, VK_CULL_MODE_BACK_BIT, }; static const VkSamplerAddressMode s_textureAddress[] = { VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, }; struct PresentMode { VkPresentModeKHR mode; bool vsync; const char* name; }; static const PresentMode s_presentMode[] = { { VK_PRESENT_MODE_FIFO_KHR, true, "VK_PRESENT_MODE_FIFO_KHR" }, { VK_PRESENT_MODE_FIFO_RELAXED_KHR, true, "VK_PRESENT_MODE_FIFO_RELAXED_KHR" }, { VK_PRESENT_MODE_MAILBOX_KHR, true, "VK_PRESENT_MODE_MAILBOX_KHR" }, { VK_PRESENT_MODE_IMMEDIATE_KHR, false, "VK_PRESENT_MODE_IMMEDIATE_KHR" }, }; #define VK_IMPORT_FUNC(_optional, _func) PFN_##_func _func #define VK_IMPORT_INSTANCE_FUNC VK_IMPORT_FUNC #define VK_IMPORT_DEVICE_FUNC VK_IMPORT_FUNC VK_IMPORT VK_IMPORT_INSTANCE VK_IMPORT_DEVICE #undef VK_IMPORT_DEVICE_FUNC #undef VK_IMPORT_INSTANCE_FUNC #undef VK_IMPORT_FUNC struct TextureFormatInfo { VkFormat m_fmt; VkFormat m_fmtSrv; VkFormat m_fmtDsv; VkFormat m_fmtSrgb; VkComponentMapping m_mapping; }; static const TextureFormatInfo s_textureFormat[] = { #define $_ VK_COMPONENT_SWIZZLE_IDENTITY #define $0 VK_COMPONENT_SWIZZLE_ZERO #define $1 VK_COMPONENT_SWIZZLE_ONE #define $R VK_COMPONENT_SWIZZLE_R #define $G VK_COMPONENT_SWIZZLE_G #define $B VK_COMPONENT_SWIZZLE_B #define $A VK_COMPONENT_SWIZZLE_A { VK_FORMAT_BC1_RGB_UNORM_BLOCK, VK_FORMAT_BC1_RGB_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_BC1_RGB_SRGB_BLOCK, { $_, $_, $_, $_ } }, // BC1 { VK_FORMAT_BC2_UNORM_BLOCK, VK_FORMAT_BC2_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_BC2_SRGB_BLOCK, { $_, $_, $_, $_ } }, // BC2 { VK_FORMAT_BC3_UNORM_BLOCK, VK_FORMAT_BC3_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_BC3_SRGB_BLOCK, { $_, $_, $_, $_ } }, // BC3 { VK_FORMAT_BC4_UNORM_BLOCK, VK_FORMAT_BC4_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // BC4 { VK_FORMAT_BC5_UNORM_BLOCK, VK_FORMAT_BC5_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // BC5 { VK_FORMAT_BC6H_SFLOAT_BLOCK, VK_FORMAT_BC6H_SFLOAT_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // BC6H { VK_FORMAT_BC7_UNORM_BLOCK, VK_FORMAT_BC7_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_BC7_SRGB_BLOCK, { $_, $_, $_, $_ } }, // BC7 { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // ETC1 { VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ETC2 { VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ETC2A { VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ETC2A1 { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // PTC12 { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // PTC14 { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // PTC12A { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // PTC14A { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // PTC22 { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // PTC24 { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // ATC { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // ATCE { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // ATCI { VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_4x4_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC4x4 { VK_FORMAT_ASTC_5x4_UNORM_BLOCK, VK_FORMAT_ASTC_5x4_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_5x4_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC5x4 { VK_FORMAT_ASTC_5x5_UNORM_BLOCK, VK_FORMAT_ASTC_5x5_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_5x5_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC5x5 { VK_FORMAT_ASTC_6x5_UNORM_BLOCK, VK_FORMAT_ASTC_6x5_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_6x5_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC6x5 { VK_FORMAT_ASTC_6x6_UNORM_BLOCK, VK_FORMAT_ASTC_6x6_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_6x6_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC6x6 { VK_FORMAT_ASTC_8x5_UNORM_BLOCK, VK_FORMAT_ASTC_8x5_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_8x5_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC8x5 { VK_FORMAT_ASTC_8x6_UNORM_BLOCK, VK_FORMAT_ASTC_8x6_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_8x6_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC8x6 { VK_FORMAT_ASTC_8x8_UNORM_BLOCK, VK_FORMAT_ASTC_8x8_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_8x8_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC8x8 { VK_FORMAT_ASTC_10x5_UNORM_BLOCK, VK_FORMAT_ASTC_10x5_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_10x5_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC10x5 { VK_FORMAT_ASTC_10x6_UNORM_BLOCK, VK_FORMAT_ASTC_10x6_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_10x6_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC10x6 { VK_FORMAT_ASTC_10x8_UNORM_BLOCK, VK_FORMAT_ASTC_10x8_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_10x8_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC10x8 { VK_FORMAT_ASTC_10x10_UNORM_BLOCK, VK_FORMAT_ASTC_10x10_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_10x10_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC10x10 { VK_FORMAT_ASTC_12x10_UNORM_BLOCK, VK_FORMAT_ASTC_12x10_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_12x10_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC12x10 { VK_FORMAT_ASTC_12x12_UNORM_BLOCK, VK_FORMAT_ASTC_12x12_UNORM_BLOCK, VK_FORMAT_UNDEFINED, VK_FORMAT_ASTC_12x12_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ASTC12x12 { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // Unknown { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R1 { VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $0, $0, $0, $R } }, // A8 { VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_R8_SRGB, { $_, $_, $_, $_ } }, // R8 { VK_FORMAT_R8_SINT, VK_FORMAT_R8_SINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R8I { VK_FORMAT_R8_UINT, VK_FORMAT_R8_UINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R8U { VK_FORMAT_R8_SNORM, VK_FORMAT_R8_SNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R8S { VK_FORMAT_R16_UNORM, VK_FORMAT_R16_UNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R16 { VK_FORMAT_R16_SINT, VK_FORMAT_R16_SINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R16I { VK_FORMAT_R16_UNORM, VK_FORMAT_R16_UNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R16U { VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R16F { VK_FORMAT_R16_SNORM, VK_FORMAT_R16_SNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R16S { VK_FORMAT_R32_SINT, VK_FORMAT_R32_SINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R32I { VK_FORMAT_R32_UINT, VK_FORMAT_R32_UINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R32U { VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32_SFLOAT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R32F { VK_FORMAT_R8G8_UNORM, VK_FORMAT_R8G8_UNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_R8G8_SRGB, { $_, $_, $_, $_ } }, // RG8 { VK_FORMAT_R8G8_SINT, VK_FORMAT_R8G8_SINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG8I { VK_FORMAT_R8G8_UINT, VK_FORMAT_R8G8_UINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG8U { VK_FORMAT_R8G8_SNORM, VK_FORMAT_R8G8_SNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG8S { VK_FORMAT_R16G16_UNORM, VK_FORMAT_R16G16_UNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG16 { VK_FORMAT_R16G16_SINT, VK_FORMAT_R16G16_SINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG16I { VK_FORMAT_R16G16_UINT, VK_FORMAT_R16G16_UINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG16U { VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG16F { VK_FORMAT_R16G16_SNORM, VK_FORMAT_R16G16_SNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG16S { VK_FORMAT_R32G32_SINT, VK_FORMAT_R32G32_SINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG32I { VK_FORMAT_R32G32_UINT, VK_FORMAT_R32G32_UINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG32U { VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG32F { VK_FORMAT_R8G8B8_UNORM, VK_FORMAT_R8G8B8_UNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_R8G8B8_SRGB, { $_, $_, $_, $_ } }, // RGB8 { VK_FORMAT_R8G8B8_SINT, VK_FORMAT_R8G8B8_SINT, VK_FORMAT_UNDEFINED, VK_FORMAT_R8G8B8_SRGB, { $_, $_, $_, $_ } }, // RGB8I { VK_FORMAT_R8G8B8_UINT, VK_FORMAT_R8G8B8_UINT, VK_FORMAT_UNDEFINED, VK_FORMAT_R8G8B8_SRGB, { $_, $_, $_, $_ } }, // RGB8U { VK_FORMAT_R8G8B8_SNORM, VK_FORMAT_R8G8B8_SNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RGB8S { VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RGB9E5F { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_B8G8R8A8_SRGB, { $_, $_, $_, $_ } }, // BGRA8 { VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_R8G8B8A8_SRGB, { $_, $_, $_, $_ } }, // RGBA8 { VK_FORMAT_R8G8B8A8_SINT, VK_FORMAT_R8G8B8A8_SINT, VK_FORMAT_UNDEFINED, VK_FORMAT_R8G8B8A8_SRGB, { $_, $_, $_, $_ } }, // RGBA8I { VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_UNDEFINED, VK_FORMAT_R8G8B8A8_SRGB, { $_, $_, $_, $_ } }, // RGBA8U { VK_FORMAT_R8G8B8A8_SNORM, VK_FORMAT_R8G8B8A8_SNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RGBA8S { VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RGBA16 { VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RGBA16I { VK_FORMAT_R16G16B16A16_UINT, VK_FORMAT_R16G16B16A16_UINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RGBA16U { VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RGBA16F { VK_FORMAT_R16G16B16A16_SNORM, VK_FORMAT_R16G16B16A16_SNORM, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RGBA16S { VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RGBA32I { VK_FORMAT_R32G32B32A32_UINT, VK_FORMAT_R32G32B32A32_UINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RGBA32U { VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RGBA32F { VK_FORMAT_R5G6B5_UNORM_PACK16, VK_FORMAT_R5G6B5_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // B5G6R5 { VK_FORMAT_B5G6R5_UNORM_PACK16, VK_FORMAT_B5G6R5_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // R5G6B5 { VK_FORMAT_B4G4R4A4_UNORM_PACK16, VK_FORMAT_B4G4R4A4_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $G, $R, $A, $B } }, // BGRA4 { VK_FORMAT_R4G4B4A4_UNORM_PACK16, VK_FORMAT_R4G4B4A4_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $A, $B, $G, $R } }, // RGBA4 { VK_FORMAT_A1R5G5B5_UNORM_PACK16, VK_FORMAT_A1R5G5B5_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // BGR5A1 { VK_FORMAT_A1R5G5B5_UNORM_PACK16, VK_FORMAT_A1R5G5B5_UNORM_PACK16, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $B, $G, $R, $A } }, // RGB5A1 { VK_FORMAT_A2R10G10B10_UNORM_PACK32, VK_FORMAT_A2R10G10B10_UNORM_PACK32, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $B, $G, $R, $A } }, // RGB10A2 { VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // RG11B10F { VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // UnknownDepth { VK_FORMAT_UNDEFINED, VK_FORMAT_R16_UNORM, VK_FORMAT_D16_UNORM, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // D16 { VK_FORMAT_UNDEFINED, VK_FORMAT_R32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // D24 { VK_FORMAT_UNDEFINED, VK_FORMAT_R32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // D24S8 { VK_FORMAT_UNDEFINED, VK_FORMAT_R32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // D32 { VK_FORMAT_UNDEFINED, VK_FORMAT_R32_SFLOAT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // D16F { VK_FORMAT_UNDEFINED, VK_FORMAT_R32_SFLOAT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // D24F { VK_FORMAT_UNDEFINED, VK_FORMAT_R32_SFLOAT, VK_FORMAT_D32_SFLOAT, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // D32F { VK_FORMAT_UNDEFINED, VK_FORMAT_R8_UINT, VK_FORMAT_S8_UINT, VK_FORMAT_UNDEFINED, { $_, $_, $_, $_ } }, // D0S8 #undef $_ #undef $0 #undef $1 #undef $R #undef $G #undef $B #undef $A }; BX_STATIC_ASSERT(TextureFormat::Count == BX_COUNTOF(s_textureFormat) ); struct LayerInfo { bool m_supported; bool m_initialize; }; struct Layer { enum Enum { VK_LAYER_LUNARG_standard_validation, VK_LAYER_KHRONOS_validation, Count }; const char* m_name; uint32_t m_minVersion; LayerInfo m_instance; LayerInfo m_device; }; // Layer registry // static Layer s_layer[] = { { "VK_LAYER_LUNARG_standard_validation", 1, { false, false }, { false, false } }, { "VK_LAYER_KHRONOS_validation", 1, { false, false }, { false, false } }, }; BX_STATIC_ASSERT(Layer::Count == BX_COUNTOF(s_layer) ); void updateLayer(const char* _name, uint32_t _version, bool _instanceLayer) { bx::StringView lyr(_name); for (uint32_t ii = 0; ii < Layer::Count; ++ii) { Layer& layer = s_layer[ii]; LayerInfo& layerInfo = _instanceLayer ? layer.m_instance : layer.m_device; if (!layerInfo.m_supported && layerInfo.m_initialize) { if ( 0 == bx::strCmp(lyr, layer.m_name) && _version >= layer.m_minVersion) { layerInfo.m_supported = true; break; } } } } struct Extension { enum Enum { EXT_debug_utils, EXT_debug_report, EXT_memory_budget, KHR_get_physical_device_properties2, EXT_conservative_rasterization, EXT_line_rasterization, EXT_shader_viewport_index_layer, EXT_custom_border_color, KHR_draw_indirect_count, Count }; const char* m_name; uint32_t m_minVersion; bool m_instanceExt; bool m_supported; bool m_initialize; Layer::Enum m_layer; }; // Extension registry // static Extension s_extension[] = { { "VK_EXT_debug_utils", 1, false, false, BGFX_CONFIG_DEBUG_OBJECT_NAME || BGFX_CONFIG_DEBUG_ANNOTATION, Layer::Count }, { "VK_EXT_debug_report", 1, false, false, false , Layer::Count }, { "VK_EXT_memory_budget", 1, false, false, true , Layer::Count }, { "VK_KHR_get_physical_device_properties2", 1, false, false, true , Layer::Count }, { "VK_EXT_conservative_rasterization", 1, false, false, true , Layer::Count }, { "VK_EXT_line_rasterization", 1, false, false, true , Layer::Count }, { "VK_EXT_shader_viewport_index_layer", 1, false, false, true , Layer::Count }, { "VK_EXT_custom_border_color", 1, false, false, true , Layer::Count }, { "VK_KHR_draw_indirect_count", 1, false, false, true , Layer::Count }, }; BX_STATIC_ASSERT(Extension::Count == BX_COUNTOF(s_extension) ); bool updateExtension(const char* _name, uint32_t _version, bool _instanceExt, Extension _extensions[Extension::Count]) { bool supported = false; if (BX_ENABLED(BGFX_CONFIG_RENDERER_USE_EXTENSIONS) ) { const bx::StringView ext(_name); for (uint32_t ii = 0; ii < Extension::Count; ++ii) { Extension& extension = _extensions[ii]; LayerInfo& layerInfo = _instanceExt ? s_layer[extension.m_layer].m_instance : s_layer[extension.m_layer].m_device ; if (!extension.m_supported && extension.m_initialize && (extension.m_layer == Layer::Count || layerInfo.m_supported) ) { if ( 0 == bx::strCmp(ext, extension.m_name) && _version >= extension.m_minVersion) { extension.m_supported = true; extension.m_instanceExt = _instanceExt; supported = true; break; } } } } return supported; } static const VkFormat s_attribType[][4][2] = { { // Uint8 { VK_FORMAT_R8_UINT, VK_FORMAT_R8_UNORM }, { VK_FORMAT_R8G8_UINT, VK_FORMAT_R8G8_UNORM }, { VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R8G8B8A8_UNORM }, { VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R8G8B8A8_UNORM }, }, { // Uint10 { VK_FORMAT_A2R10G10B10_UINT_PACK32, VK_FORMAT_A2R10G10B10_UNORM_PACK32 }, { VK_FORMAT_A2R10G10B10_UINT_PACK32, VK_FORMAT_A2R10G10B10_UNORM_PACK32 }, { VK_FORMAT_A2R10G10B10_UINT_PACK32, VK_FORMAT_A2R10G10B10_UNORM_PACK32 }, { VK_FORMAT_A2R10G10B10_UINT_PACK32, VK_FORMAT_A2R10G10B10_UNORM_PACK32 }, }, { // Int16 { VK_FORMAT_R16_SINT, VK_FORMAT_R16_SNORM }, { VK_FORMAT_R16G16_SINT, VK_FORMAT_R16G16_SNORM }, { VK_FORMAT_R16G16B16_SINT, VK_FORMAT_R16G16B16_SNORM }, { VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_R16G16B16A16_SNORM }, }, { // Half { VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16_SFLOAT }, { VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_R16G16_SFLOAT }, { VK_FORMAT_R16G16B16_SFLOAT, VK_FORMAT_R16G16B16_SFLOAT }, { VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT }, }, { // Float { VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32_SFLOAT }, { VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32_SFLOAT }, { VK_FORMAT_R32G32B32_SFLOAT, VK_FORMAT_R32G32B32_SFLOAT }, { VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R32G32B32A32_SFLOAT }, }, }; BX_STATIC_ASSERT(AttribType::Count == BX_COUNTOF(s_attribType) ); void fillVertexLayout(const ShaderVK* _vsh, VkPipelineVertexInputStateCreateInfo& _vertexInputState, const VertexLayout& _layout) { uint32_t numBindings = _vertexInputState.vertexBindingDescriptionCount; uint32_t numAttribs = _vertexInputState.vertexAttributeDescriptionCount; VkVertexInputBindingDescription* inputBinding = const_cast<VkVertexInputBindingDescription*>(_vertexInputState.pVertexBindingDescriptions + numBindings); VkVertexInputAttributeDescription* inputAttrib = const_cast<VkVertexInputAttributeDescription*>(_vertexInputState.pVertexAttributeDescriptions + numAttribs); inputBinding->binding = numBindings; inputBinding->stride = _layout.m_stride; inputBinding->inputRate = VK_VERTEX_INPUT_RATE_VERTEX; for (uint32_t attr = 0; attr < Attrib::Count; ++attr) { if (UINT16_MAX != _layout.m_attributes[attr]) { inputAttrib->location = _vsh->m_attrRemap[attr]; inputAttrib->binding = numBindings; uint8_t num; AttribType::Enum type; bool normalized; bool asInt; _layout.decode(Attrib::Enum(attr), num, type, normalized, asInt); inputAttrib->format = s_attribType[type][num-1][normalized]; inputAttrib->offset = _layout.m_offset[attr]; ++inputAttrib; ++numAttribs; } } _vertexInputState.vertexBindingDescriptionCount = numBindings + 1; _vertexInputState.vertexAttributeDescriptionCount = numAttribs; } void fillInstanceBinding(const ShaderVK* _vsh, VkPipelineVertexInputStateCreateInfo& _vertexInputState, uint32_t _numInstanceData) { BX_UNUSED(_vsh); uint32_t numBindings = _vertexInputState.vertexBindingDescriptionCount; uint32_t numAttribs = _vertexInputState.vertexAttributeDescriptionCount; VkVertexInputBindingDescription* inputBinding = const_cast<VkVertexInputBindingDescription*>(_vertexInputState.pVertexBindingDescriptions + numBindings); VkVertexInputAttributeDescription* inputAttrib = const_cast<VkVertexInputAttributeDescription*>(_vertexInputState.pVertexAttributeDescriptions + numAttribs); inputBinding->binding = numBindings; inputBinding->stride = _numInstanceData * 16; inputBinding->inputRate = VK_VERTEX_INPUT_RATE_INSTANCE; for (uint32_t inst = 0; inst < _numInstanceData; ++inst) { inputAttrib->location = numAttribs; inputAttrib->binding = numBindings; inputAttrib->format = VK_FORMAT_R32G32B32A32_SFLOAT; inputAttrib->offset = inst * 16; ++numAttribs; ++inputAttrib; } _vertexInputState.vertexBindingDescriptionCount = numBindings + 1; _vertexInputState.vertexAttributeDescriptionCount = numAttribs; } static const char* s_deviceTypeName[] = { "Other", "Integrated GPU", "Discrete GPU", "Virtual GPU", "CPU", "Unknown?!" }; const char* getName(VkPhysicalDeviceType _type) { return s_deviceTypeName[bx::min<int32_t>(_type, BX_COUNTOF(s_deviceTypeName)-1 )]; } static const char* s_allocScopeName[] = { "vkCommand", "vkObject", "vkCache", "vkDevice", "vkInstance", }; BX_STATIC_ASSERT(VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE == BX_COUNTOF(s_allocScopeName)-1); static void* VKAPI_PTR allocationFunction(void* _userData, size_t _size, size_t _alignment, VkSystemAllocationScope _allocationScope) { BX_UNUSED(_userData, _allocationScope); return bx::alignedAlloc(g_allocator, _size, _alignment, s_allocScopeName[_allocationScope]); } static void* VKAPI_PTR reallocationFunction(void* _userData, void* _original, size_t _size, size_t _alignment, VkSystemAllocationScope _allocationScope) { BX_UNUSED(_userData, _allocationScope); return bx::alignedRealloc(g_allocator, _original, _size, _alignment, s_allocScopeName[_allocationScope]); } static void VKAPI_PTR freeFunction(void* _userData, void* _memory) { BX_UNUSED(_userData); if (NULL == _memory) { return; } bx::alignedFree(g_allocator, _memory, 8); } static void VKAPI_PTR internalAllocationNotification(void* _userData, size_t _size, VkInternalAllocationType _allocationType, VkSystemAllocationScope _allocationScope) { BX_UNUSED(_userData, _size, _allocationType, _allocationScope); } static void VKAPI_PTR internalFreeNotification(void* _userData, size_t _size, VkInternalAllocationType _allocationType, VkSystemAllocationScope _allocationScope) { BX_UNUSED(_userData, _size, _allocationType, _allocationScope); } static VkAllocationCallbacks s_allocationCb = { NULL, allocationFunction, reallocationFunction, freeFunction, internalAllocationNotification, internalFreeNotification, }; VkResult VKAPI_PTR stubSetDebugUtilsObjectNameEXT(VkDevice _device, const VkDebugUtilsObjectNameInfoEXT* _nameInfo) { BX_UNUSED(_device, _nameInfo); return VK_SUCCESS; } void VKAPI_PTR stubCmdInsertDebugUtilsLabelEXT(VkCommandBuffer _commandBuffer, const VkDebugUtilsLabelEXT* _labelInfo) { BX_UNUSED(_commandBuffer, _labelInfo); } void VKAPI_PTR stubCmdBeginDebugUtilsLabelEXT(VkCommandBuffer _commandBuffer, const VkDebugUtilsLabelEXT* _labelInfo) { BX_UNUSED(_commandBuffer, _labelInfo); } void VKAPI_PTR stubCmdEndDebugUtilsLabelEXT(VkCommandBuffer _commandBuffer) { BX_UNUSED(_commandBuffer); } static const char* s_debugReportObjectType[] = { "Unknown", "Instance", "PhysicalDevice", "Device", "Queue", "Semaphore", "CommandBuffer", "Fence", "DeviceMemory", "Buffer", "Image", "Event", "QueryPool", "BufferView", "ImageView", "ShaderModule", "PipelineCache", "PipelineLayout", "RenderPass", "Pipeline", "DescriptorSetLayout", "Sampler", "DescriptorPool", "DescriptorSet", "Framebuffer", "CommandPool", "SurfaceKHR", "SwapchainKHR", "DebugReport", }; VkBool32 VKAPI_PTR debugReportCb( VkDebugReportFlagsEXT _flags, VkDebugReportObjectTypeEXT _objectType, uint64_t _object, size_t _location, int32_t _messageCode, const char* _layerPrefix, const char* _message, void* _userData ) { BX_UNUSED(_flags , _objectType , _object , _location , _messageCode , _layerPrefix , _message , _userData , s_debugReportObjectType ); // For more info about 'VUID-VkSwapchainCreateInfoKHR-imageExtent-01274' // check https://github.com/KhronosGroup/Vulkan-Docs/issues/1144 if (!bx::strFind(_message, "PointSizeMissing").isEmpty() || !bx::strFind(_message, "SwapchainTooManyImages").isEmpty() || !bx::strFind(_message, "SwapchainImageNotAcquired").isEmpty() || !bx::strFind(_message, "VUID-VkSwapchainCreateInfoKHR-imageExtent-01274").isEmpty() ) { return VK_FALSE; } BX_TRACE("%c%c%c%c%c %19s, %s, %d: %s" , 0 != (_flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT ) ? 'I' : '-' , 0 != (_flags & VK_DEBUG_REPORT_WARNING_BIT_EXT ) ? 'W' : '-' , 0 != (_flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) ? 'P' : '-' , 0 != (_flags & VK_DEBUG_REPORT_ERROR_BIT_EXT ) ? 'E' : '-' , 0 != (_flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT ) ? 'D' : '-' , s_debugReportObjectType[_objectType] , _layerPrefix , _messageCode , _message ); return VK_FALSE; } VkResult enumerateLayerProperties(VkPhysicalDevice _physicalDevice, uint32_t* _propertyCount, VkLayerProperties* _properties) { return (VK_NULL_HANDLE == _physicalDevice) ? vkEnumerateInstanceLayerProperties(_propertyCount, _properties) : vkEnumerateDeviceLayerProperties(_physicalDevice, _propertyCount, _properties) ; } VkResult enumerateExtensionProperties(VkPhysicalDevice _physicalDevice, const char* _layerName, uint32_t* _propertyCount, VkExtensionProperties* _properties) { return (VK_NULL_HANDLE == _physicalDevice) ? vkEnumerateInstanceExtensionProperties(_layerName, _propertyCount, _properties) : vkEnumerateDeviceExtensionProperties(_physicalDevice, _layerName, _propertyCount, _properties) ; } void dumpExtensions(VkPhysicalDevice _physicalDevice, Extension _extensions[Extension::Count]) { { // Global extensions. uint32_t numExtensionProperties; VkResult result = enumerateExtensionProperties(_physicalDevice , NULL , &numExtensionProperties , NULL ); if (VK_SUCCESS == result && 0 < numExtensionProperties) { VkExtensionProperties* extensionProperties = (VkExtensionProperties*)BX_ALLOC(g_allocator, numExtensionProperties * sizeof(VkExtensionProperties) ); result = enumerateExtensionProperties(_physicalDevice , NULL , &numExtensionProperties , extensionProperties ); BX_TRACE("Global extensions (%d):" , numExtensionProperties ); for (uint32_t extension = 0; extension < numExtensionProperties; ++extension) { bool supported = updateExtension( extensionProperties[extension].extensionName , extensionProperties[extension].specVersion , VK_NULL_HANDLE == _physicalDevice , _extensions ); BX_TRACE("\tv%-3d %s%s" , extensionProperties[extension].specVersion , extensionProperties[extension].extensionName , supported ? " (supported)" : "", extensionProperties[extension].extensionName ); BX_UNUSED(supported); } BX_FREE(g_allocator, extensionProperties); } } // Layer extensions. uint32_t numLayerProperties; VkResult result = enumerateLayerProperties(_physicalDevice, &numLayerProperties, NULL); if (VK_SUCCESS == result && 0 < numLayerProperties) { VkLayerProperties* layerProperties = (VkLayerProperties*)BX_ALLOC(g_allocator, numLayerProperties * sizeof(VkLayerProperties) ); result = enumerateLayerProperties(_physicalDevice, &numLayerProperties, layerProperties); char indent = VK_NULL_HANDLE == _physicalDevice ? '\0' : '\t'; BX_UNUSED(indent); BX_TRACE("%cLayer extensions (%d):" , indent , numLayerProperties ); for (uint32_t layer = 0; layer < numLayerProperties; ++layer) { updateLayer( layerProperties[layer].layerName , layerProperties[layer].implementationVersion , VK_NULL_HANDLE == _physicalDevice ); BX_TRACE("%c\t%s (s: 0x%08x, i: 0x%08x), %s" , indent , layerProperties[layer].layerName , layerProperties[layer].specVersion , layerProperties[layer].implementationVersion , layerProperties[layer].description ); uint32_t numExtensionProperties; result = enumerateExtensionProperties(_physicalDevice , layerProperties[layer].layerName , &numExtensionProperties , NULL ); if (VK_SUCCESS == result && 0 < numExtensionProperties) { VkExtensionProperties* extensionProperties = (VkExtensionProperties*)BX_ALLOC(g_allocator, numExtensionProperties * sizeof(VkExtensionProperties) ); result = enumerateExtensionProperties(_physicalDevice , layerProperties[layer].layerName , &numExtensionProperties , extensionProperties ); for (uint32_t extension = 0; extension < numExtensionProperties; ++extension) { bool supported = updateExtension( extensionProperties[extension].extensionName , extensionProperties[extension].specVersion , VK_NULL_HANDLE == _physicalDevice , _extensions ); BX_TRACE("%c\t\t%s (s: 0x%08x)" , indent , extensionProperties[extension].extensionName , extensionProperties[extension].specVersion , supported ? " (supported)" : "", extensionProperties[extension].extensionName ); BX_UNUSED(supported); } BX_FREE(g_allocator, extensionProperties); } } BX_FREE(g_allocator, layerProperties); } } const char* getName(VkResult _result) { switch (_result) { #define VKENUM(_ty) case _ty: return #_ty VKENUM(VK_SUCCESS); VKENUM(VK_NOT_READY); VKENUM(VK_TIMEOUT); VKENUM(VK_EVENT_SET); VKENUM(VK_EVENT_RESET); VKENUM(VK_INCOMPLETE); VKENUM(VK_ERROR_OUT_OF_HOST_MEMORY); VKENUM(VK_ERROR_OUT_OF_DEVICE_MEMORY); VKENUM(VK_ERROR_OUT_OF_POOL_MEMORY); VKENUM(VK_ERROR_FRAGMENTED_POOL); VKENUM(VK_ERROR_INITIALIZATION_FAILED); VKENUM(VK_ERROR_DEVICE_LOST); VKENUM(VK_ERROR_MEMORY_MAP_FAILED); VKENUM(VK_ERROR_LAYER_NOT_PRESENT); VKENUM(VK_ERROR_EXTENSION_NOT_PRESENT); VKENUM(VK_ERROR_FEATURE_NOT_PRESENT); VKENUM(VK_ERROR_INCOMPATIBLE_DRIVER); VKENUM(VK_ERROR_TOO_MANY_OBJECTS); VKENUM(VK_ERROR_FORMAT_NOT_SUPPORTED); VKENUM(VK_ERROR_SURFACE_LOST_KHR); VKENUM(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); VKENUM(VK_SUBOPTIMAL_KHR); VKENUM(VK_ERROR_OUT_OF_DATE_KHR); VKENUM(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); VKENUM(VK_ERROR_VALIDATION_FAILED_EXT); #undef VKENUM default: break; } BX_WARN(false, "Unknown VkResult? %x", _result); return "<VkResult?>"; } template<typename Ty> constexpr VkObjectType getType(); template<> VkObjectType getType<VkBuffer >() { return VK_OBJECT_TYPE_BUFFER; } template<> VkObjectType getType<VkCommandPool >() { return VK_OBJECT_TYPE_COMMAND_POOL; } template<> VkObjectType getType<VkDescriptorPool >() { return VK_OBJECT_TYPE_DESCRIPTOR_POOL; } template<> VkObjectType getType<VkDescriptorSet >() { return VK_OBJECT_TYPE_DESCRIPTOR_SET; } template<> VkObjectType getType<VkDescriptorSetLayout>() { return VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT; } template<> VkObjectType getType<VkDeviceMemory >() { return VK_OBJECT_TYPE_DEVICE_MEMORY; } template<> VkObjectType getType<VkFence >() { return VK_OBJECT_TYPE_FENCE; } template<> VkObjectType getType<VkFramebuffer >() { return VK_OBJECT_TYPE_FRAMEBUFFER; } template<> VkObjectType getType<VkImage >() { return VK_OBJECT_TYPE_IMAGE; } template<> VkObjectType getType<VkImageView >() { return VK_OBJECT_TYPE_IMAGE_VIEW; } template<> VkObjectType getType<VkPipeline >() { return VK_OBJECT_TYPE_PIPELINE; } template<> VkObjectType getType<VkPipelineCache >() { return VK_OBJECT_TYPE_PIPELINE_CACHE; } template<> VkObjectType getType<VkPipelineLayout >() { return VK_OBJECT_TYPE_PIPELINE_LAYOUT; } template<> VkObjectType getType<VkQueryPool >() { return VK_OBJECT_TYPE_QUERY_POOL; } template<> VkObjectType getType<VkRenderPass >() { return VK_OBJECT_TYPE_RENDER_PASS; } template<> VkObjectType getType<VkSampler >() { return VK_OBJECT_TYPE_SAMPLER; } template<> VkObjectType getType<VkSemaphore >() { return VK_OBJECT_TYPE_SEMAPHORE; } template<> VkObjectType getType<VkShaderModule >() { return VK_OBJECT_TYPE_SHADER_MODULE; } template<> VkObjectType getType<VkSurfaceKHR >() { return VK_OBJECT_TYPE_SURFACE_KHR; } template<> VkObjectType getType<VkSwapchainKHR >() { return VK_OBJECT_TYPE_SWAPCHAIN_KHR; } template<typename Ty> static BX_NO_INLINE void setDebugObjectName(VkDevice _device, Ty _object, const char* _format, ...) { if (BX_ENABLED(BGFX_CONFIG_DEBUG_OBJECT_NAME) && s_extension[Extension::EXT_debug_utils].m_supported) { char temp[2048]; va_list argList; va_start(argList, _format); int32_t size = bx::min<int32_t>(sizeof(temp)-1, bx::vsnprintf(temp, sizeof(temp), _format, argList) ); va_end(argList); temp[size] = '\0'; VkDebugUtilsObjectNameInfoEXT ni; ni.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; ni.pNext = NULL; ni.objectType = getType<Ty>(); ni.objectHandle = uint64_t(_object.vk); ni.pObjectName = temp; VK_CHECK(vkSetDebugUtilsObjectNameEXT(_device, &ni) ); } } void setMemoryBarrier( VkCommandBuffer _commandBuffer , VkPipelineStageFlags _srcStages , VkPipelineStageFlags _dstStages ) { VkMemoryBarrier mb; mb.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; mb.pNext = NULL; mb.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT; mb.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; vkCmdPipelineBarrier( _commandBuffer , _srcStages , _dstStages , 0 , 1 , &mb , 0 , NULL , 0 , NULL ); } void setImageMemoryBarrier( VkCommandBuffer _commandBuffer , VkImage _image , VkImageAspectFlags _aspectMask , VkImageLayout _oldLayout , VkImageLayout _newLayout , uint32_t _baseMipLevel = 0 , uint32_t _levelCount = VK_REMAINING_MIP_LEVELS , uint32_t _baseArrayLayer = 0 , uint32_t _layerCount = VK_REMAINING_ARRAY_LAYERS ) { BX_ASSERT(true && _newLayout != VK_IMAGE_LAYOUT_UNDEFINED && _newLayout != VK_IMAGE_LAYOUT_PREINITIALIZED , "_newLayout cannot use VK_IMAGE_LAYOUT_UNDEFINED or VK_IMAGE_LAYOUT_PREINITIALIZED." ); constexpr VkPipelineStageFlags depthStageMask = 0 | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT ; constexpr VkPipelineStageFlags sampledStageMask = 0 | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT ; VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; VkAccessFlags srcAccessMask = 0; VkAccessFlags dstAccessMask = 0; switch (_oldLayout) { case VK_IMAGE_LAYOUT_UNDEFINED: break; case VK_IMAGE_LAYOUT_GENERAL: srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT; break; case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: srcStageMask = depthStageMask; srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL: srcStageMask = depthStageMask | sampledStageMask; break; case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: srcStageMask = sampledStageMask; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; break; case VK_IMAGE_LAYOUT_PREINITIALIZED: srcStageMask = VK_PIPELINE_STAGE_HOST_BIT; srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; break; case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: break; default: BX_ASSERT(false, "Unknown image layout."); break; } switch (_newLayout) { case VK_IMAGE_LAYOUT_GENERAL: dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; break; case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: dstStageMask = depthStageMask; dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL: dstStageMask = depthStageMask | sampledStageMask; dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; break; case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: dstStageMask = sampledStageMask; dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL: dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; break; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; break; case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: // vkQueuePresentKHR performs automatic visibility operations break; default: BX_ASSERT(false, "Unknown image layout."); break; } VkImageMemoryBarrier imb; imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imb.pNext = NULL; imb.srcAccessMask = srcAccessMask; imb.dstAccessMask = dstAccessMask; imb.oldLayout = _oldLayout; imb.newLayout = _newLayout; imb.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; imb.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; imb.image = _image; imb.subresourceRange.aspectMask = _aspectMask; imb.subresourceRange.baseMipLevel = _baseMipLevel; imb.subresourceRange.levelCount = _levelCount; imb.subresourceRange.baseArrayLayer = _baseArrayLayer; imb.subresourceRange.layerCount = _layerCount; vkCmdPipelineBarrier(_commandBuffer , srcStageMask , dstStageMask , 0 , 0 , NULL , 0 , NULL , 1 , &imb ); } #define MAX_DESCRIPTOR_SETS (1024 * BGFX_CONFIG_MAX_FRAME_LATENCY) struct RendererContextVK : public RendererContextI { RendererContextVK() : m_allocatorCb(NULL) , m_renderDocDll(NULL) , m_vulkan1Dll(NULL) , m_maxAnisotropy(1.0f) , m_depthClamp(false) , m_wireframe(false) , m_captureBuffer(VK_NULL_HANDLE) , m_captureMemory(VK_NULL_HANDLE) , m_captureSize(0) { } ~RendererContextVK() { } bool init(const Init& _init) { struct ErrorState { enum Enum { Default, LoadedVulkan1, InstanceCreated, DeviceCreated, CommandQueueCreated, SwapChainCreated, DescriptorCreated, TimerQueryCreated, }; }; ErrorState::Enum errorState = ErrorState::Default; const bool headless = NULL == g_platformData.nwh; const void* nextFeatures = NULL; VkPhysicalDeviceLineRasterizationFeaturesEXT lineRasterizationFeatures; VkPhysicalDeviceCustomBorderColorFeaturesEXT customBorderColorFeatures; bx::memSet(&lineRasterizationFeatures, 0, sizeof(lineRasterizationFeatures) ); bx::memSet(&customBorderColorFeatures, 0, sizeof(customBorderColorFeatures) ); m_fbh.idx = kInvalidHandle; bx::memSet(m_uniforms, 0, sizeof(m_uniforms) ); bx::memSet(&m_resolution, 0, sizeof(m_resolution) ); bool imported = true; VkResult result; m_globalQueueFamily = UINT32_MAX; if (_init.debug || _init.profile) { m_renderDocDll = loadRenderDoc(); } setGraphicsDebuggerPresent(NULL != m_renderDocDll); m_vulkan1Dll = bx::dlopen( #if BX_PLATFORM_WINDOWS "vulkan-1.dll" #elif BX_PLATFORM_ANDROID "libvulkan.so" #elif BX_PLATFORM_OSX "libvulkan.dylib" #else "libvulkan.so.1" #endif // BX_PLATFORM_* ); if (NULL == m_vulkan1Dll) { BX_TRACE("Init error: Failed to load vulkan dynamic library."); goto error; } errorState = ErrorState::LoadedVulkan1; BX_TRACE("Shared library functions:"); #define VK_IMPORT_FUNC(_optional, _func) \ _func = (PFN_##_func)bx::dlsym(m_vulkan1Dll, #_func); \ BX_TRACE("\t%p " #_func, _func); \ imported &= _optional || NULL != _func VK_IMPORT #undef VK_IMPORT_FUNC if (!imported) { BX_TRACE("Init error: Failed to load shared library functions."); goto error; } { s_layer[Layer::VK_LAYER_LUNARG_standard_validation].m_device.m_initialize = _init.debug; s_layer[Layer::VK_LAYER_LUNARG_standard_validation].m_instance.m_initialize = _init.debug; s_layer[Layer::VK_LAYER_KHRONOS_validation ].m_device.m_initialize = _init.debug; s_layer[Layer::VK_LAYER_KHRONOS_validation ].m_instance.m_initialize = _init.debug; s_extension[Extension::EXT_debug_report].m_initialize = _init.debug; s_extension[Extension::EXT_shader_viewport_index_layer].m_initialize = !!(_init.capabilities & BGFX_CAPS_VIEWPORT_LAYER_ARRAY); s_extension[Extension::EXT_conservative_rasterization ].m_initialize = !!(_init.capabilities & BGFX_CAPS_CONSERVATIVE_RASTER ); s_extension[Extension::KHR_draw_indirect_count ].m_initialize = !!(_init.capabilities & BGFX_CAPS_DRAW_INDIRECT_COUNT ); dumpExtensions(VK_NULL_HANDLE, s_extension); if (s_layer[Layer::VK_LAYER_KHRONOS_validation].m_device.m_supported || s_layer[Layer::VK_LAYER_KHRONOS_validation].m_instance.m_supported) { s_layer[Layer::VK_LAYER_LUNARG_standard_validation].m_device.m_supported = false; s_layer[Layer::VK_LAYER_LUNARG_standard_validation].m_instance.m_supported = false; } uint32_t numEnabledLayers = 0; const char* enabledLayer[Layer::Count]; BX_TRACE("Enabled instance layers:"); for (uint32_t ii = 0; ii < Layer::Count; ++ii) { const Layer& layer = s_layer[ii]; if (layer.m_instance.m_supported && layer.m_instance.m_initialize) { enabledLayer[numEnabledLayers++] = layer.m_name; BX_TRACE("\t%s", layer.m_name); } } uint32_t numEnabledExtensions = headless ? 0 : 2; const char* enabledExtension[Extension::Count + 2] = { VK_KHR_SURFACE_EXTENSION_NAME, KHR_SURFACE_EXTENSION_NAME, }; for (uint32_t ii = 0; ii < Extension::Count; ++ii) { const Extension& extension = s_extension[ii]; const LayerInfo& layerInfo = s_layer[extension.m_layer].m_instance; const bool layerEnabled = false || extension.m_layer == Layer::Count || (layerInfo.m_supported && layerInfo.m_initialize) ; if (extension.m_supported && extension.m_initialize && extension.m_instanceExt && layerEnabled) { enabledExtension[numEnabledExtensions++] = extension.m_name; } } BX_TRACE("Enabled instance extensions:"); for (uint32_t ii = 0; ii < numEnabledExtensions; ++ii) { BX_TRACE("\t%s", enabledExtension[ii]); } uint32_t vulkanApiVersionSelector; if (NULL != vkEnumerateInstanceVersion) { result = vkEnumerateInstanceVersion(&vulkanApiVersionSelector); if (VK_SUCCESS != result) { BX_TRACE( "Init error: vkEnumerateInstanceVersion failed %d: %s." , result , getName(result) ); goto error; } } else { vulkanApiVersionSelector = VK_API_VERSION_1_0; } VkApplicationInfo appInfo; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pNext = NULL; appInfo.pApplicationName = "bgfx"; appInfo.applicationVersion = BGFX_API_VERSION; appInfo.pEngineName = "bgfx"; appInfo.engineVersion = BGFX_API_VERSION; appInfo.apiVersion = vulkanApiVersionSelector; VkInstanceCreateInfo ici; ici.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; ici.pNext = NULL; ici.flags = 0; ici.pApplicationInfo = &appInfo; ici.enabledLayerCount = numEnabledLayers; ici.ppEnabledLayerNames = enabledLayer; ici.enabledExtensionCount = numEnabledExtensions; ici.ppEnabledExtensionNames = enabledExtension; if (BX_ENABLED(BGFX_CONFIG_DEBUG) ) { m_allocatorCb = &s_allocationCb; BX_UNUSED(s_allocationCb); } result = vkCreateInstance( &ici , m_allocatorCb , &m_instance ); if (VK_SUCCESS != result) { BX_TRACE("Init error: vkCreateInstance failed %d: %s.", result, getName(result) ); goto error; } m_instanceApiVersion = vulkanApiVersionSelector; BX_TRACE("Instance API version: %d.%d.%d" , VK_API_VERSION_MAJOR(m_instanceApiVersion) , VK_API_VERSION_MINOR(m_instanceApiVersion) , VK_API_VERSION_PATCH(m_instanceApiVersion) ); BX_TRACE("Instance variant: %d", VK_API_VERSION_VARIANT(m_instanceApiVersion) ); } errorState = ErrorState::InstanceCreated; BX_TRACE("Instance functions:"); #define VK_IMPORT_INSTANCE_FUNC(_optional, _func) \ _func = (PFN_##_func)vkGetInstanceProcAddr(m_instance, #_func); \ BX_TRACE("\t%p " #_func, _func); \ imported &= _optional || NULL != _func VK_IMPORT_INSTANCE #undef VK_IMPORT_INSTANCE_FUNC if (!imported) { BX_TRACE("Init error: Failed to load instance functions."); goto error; } m_debugReportCallback = VK_NULL_HANDLE; if (s_extension[Extension::EXT_debug_report].m_supported) { VkDebugReportCallbackCreateInfoEXT drcb; drcb.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; drcb.pNext = NULL; drcb.pfnCallback = debugReportCb; drcb.pUserData = NULL; drcb.flags = 0 | VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT ; result = vkCreateDebugReportCallbackEXT(m_instance , &drcb , m_allocatorCb , &m_debugReportCallback ); BX_WARN(VK_SUCCESS == result, "vkCreateDebugReportCallbackEXT failed %d: %s.", result, getName(result) ); } { BX_TRACE("---"); uint32_t numPhysicalDevices; result = vkEnumeratePhysicalDevices(m_instance , &numPhysicalDevices , NULL ); if (VK_SUCCESS != result) { BX_TRACE("Init error: vkEnumeratePhysicalDevices failed %d: %s.", result, getName(result) ); goto error; } VkPhysicalDevice physicalDevices[4]; numPhysicalDevices = bx::min<uint32_t>(numPhysicalDevices, BX_COUNTOF(physicalDevices) ); result = vkEnumeratePhysicalDevices(m_instance , &numPhysicalDevices , physicalDevices ); if (VK_SUCCESS != result) { BX_TRACE("Init error: vkEnumeratePhysicalDevices failed %d: %s.", result, getName(result) ); goto error; } Extension physicalDeviceExtensions[4][Extension::Count]; uint32_t physicalDeviceIdx = UINT32_MAX; uint32_t fallbackPhysicalDeviceIdx = UINT32_MAX; for (uint32_t ii = 0; ii < numPhysicalDevices; ++ii) { VkPhysicalDeviceProperties pdp; vkGetPhysicalDeviceProperties(physicalDevices[ii], &pdp); BX_TRACE("Physical device %d:", ii); BX_TRACE("\t Name: %s", pdp.deviceName); BX_TRACE("\t API version: %d.%d.%d" , VK_API_VERSION_MAJOR(pdp.apiVersion) , VK_API_VERSION_MINOR(pdp.apiVersion) , VK_API_VERSION_PATCH(pdp.apiVersion) ); BX_TRACE("\t API variant: %d", VK_API_VERSION_VARIANT(pdp.apiVersion) ); BX_TRACE("\tDriver version: %x", pdp.driverVersion); BX_TRACE("\t VendorId: %x", pdp.vendorID); BX_TRACE("\t DeviceId: %x", pdp.deviceID); BX_TRACE("\t Type: %d", pdp.deviceType); g_caps.gpu[ii].vendorId = uint16_t(pdp.vendorID); g_caps.gpu[ii].deviceId = uint16_t(pdp.deviceID); ++g_caps.numGPUs; if ( (BGFX_PCI_ID_NONE != g_caps.vendorId || 0 != g_caps.deviceId) && (BGFX_PCI_ID_NONE == g_caps.vendorId || pdp.vendorID == g_caps.vendorId) && (0 == g_caps.deviceId || pdp.deviceID == g_caps.deviceId) ) { if (pdp.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || pdp.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { fallbackPhysicalDeviceIdx = ii; } physicalDeviceIdx = ii; } else { if (pdp.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { fallbackPhysicalDeviceIdx = ii; } else if (pdp.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { physicalDeviceIdx = ii; } } VkPhysicalDeviceMemoryProperties pdmp; vkGetPhysicalDeviceMemoryProperties(physicalDevices[ii], &pdmp); BX_TRACE("\tMemory type count: %d", pdmp.memoryTypeCount); for (uint32_t jj = 0; jj < pdmp.memoryTypeCount; ++jj) { BX_TRACE("\t%3d: flags 0x%08x, index %d" , jj , pdmp.memoryTypes[jj].propertyFlags , pdmp.memoryTypes[jj].heapIndex ); } BX_TRACE("\tMemory heap count: %d", pdmp.memoryHeapCount); for (uint32_t jj = 0; jj < pdmp.memoryHeapCount; ++jj) { char size[16]; bx::prettify(size, BX_COUNTOF(size), pdmp.memoryHeaps[jj].size); BX_TRACE("\t%3d: flags 0x%08x, size %10s" , jj , pdmp.memoryHeaps[jj].flags , size ); } bx::memCopy(&physicalDeviceExtensions[ii][0], &s_extension[0], sizeof(s_extension) ); dumpExtensions(physicalDevices[ii], physicalDeviceExtensions[ii]); } if (UINT32_MAX == physicalDeviceIdx) { physicalDeviceIdx = UINT32_MAX == fallbackPhysicalDeviceIdx ? 0 : fallbackPhysicalDeviceIdx ; } m_physicalDevice = physicalDevices[physicalDeviceIdx]; bx::memCopy(&s_extension[0], &physicalDeviceExtensions[physicalDeviceIdx][0], sizeof(s_extension) ); vkGetPhysicalDeviceProperties(m_physicalDevice, &m_deviceProperties); g_caps.vendorId = uint16_t(m_deviceProperties.vendorID); g_caps.deviceId = uint16_t(m_deviceProperties.deviceID); BX_TRACE("Using physical device %d: %s", physicalDeviceIdx, m_deviceProperties.deviceName); VkPhysicalDeviceFeatures supportedFeatures; if (s_extension[Extension::KHR_get_physical_device_properties2].m_supported) { VkPhysicalDeviceFeatures2KHR deviceFeatures2; deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; deviceFeatures2.pNext = NULL; VkBaseOutStructure* next = (VkBaseOutStructure*)&deviceFeatures2; if (s_extension[Extension::EXT_line_rasterization].m_supported) { next->pNext = (VkBaseOutStructure*)&lineRasterizationFeatures; next = (VkBaseOutStructure*)&lineRasterizationFeatures; lineRasterizationFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT; lineRasterizationFeatures.pNext = NULL; } if (s_extension[Extension::EXT_custom_border_color].m_supported) { next->pNext = (VkBaseOutStructure*)&customBorderColorFeatures; next = (VkBaseOutStructure*)&customBorderColorFeatures; customBorderColorFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT; customBorderColorFeatures.pNext = NULL; } nextFeatures = deviceFeatures2.pNext; vkGetPhysicalDeviceFeatures2KHR(m_physicalDevice, &deviceFeatures2); supportedFeatures = deviceFeatures2.features; } else { vkGetPhysicalDeviceFeatures(m_physicalDevice, &supportedFeatures); } bx::memSet(&m_deviceFeatures, 0, sizeof(m_deviceFeatures) ); m_deviceFeatures.fullDrawIndexUint32 = supportedFeatures.fullDrawIndexUint32; m_deviceFeatures.imageCubeArray = supportedFeatures.imageCubeArray && (_init.capabilities & BGFX_CAPS_TEXTURE_CUBE_ARRAY); m_deviceFeatures.independentBlend = supportedFeatures.independentBlend && (_init.capabilities & BGFX_CAPS_BLEND_INDEPENDENT); m_deviceFeatures.multiDrawIndirect = supportedFeatures.multiDrawIndirect && (_init.capabilities & BGFX_CAPS_DRAW_INDIRECT); m_deviceFeatures.drawIndirectFirstInstance = supportedFeatures.drawIndirectFirstInstance && (_init.capabilities & BGFX_CAPS_DRAW_INDIRECT); m_deviceFeatures.depthClamp = supportedFeatures.depthClamp; m_deviceFeatures.fillModeNonSolid = supportedFeatures.fillModeNonSolid; m_deviceFeatures.largePoints = supportedFeatures.largePoints; m_deviceFeatures.samplerAnisotropy = supportedFeatures.samplerAnisotropy; m_deviceFeatures.textureCompressionETC2 = supportedFeatures.textureCompressionETC2; m_deviceFeatures.textureCompressionBC = supportedFeatures.textureCompressionBC; m_deviceFeatures.vertexPipelineStoresAndAtomics = supportedFeatures.vertexPipelineStoresAndAtomics; m_deviceFeatures.fragmentStoresAndAtomics = supportedFeatures.fragmentStoresAndAtomics; m_deviceFeatures.shaderImageGatherExtended = supportedFeatures.shaderImageGatherExtended; m_deviceFeatures.shaderStorageImageExtendedFormats = supportedFeatures.shaderStorageImageExtendedFormats; m_deviceFeatures.shaderClipDistance = supportedFeatures.shaderClipDistance; m_deviceFeatures.shaderCullDistance = supportedFeatures.shaderCullDistance; m_deviceFeatures.shaderResourceMinLod = supportedFeatures.shaderResourceMinLod; m_lineAASupport = true && s_extension[Extension::EXT_line_rasterization].m_supported && lineRasterizationFeatures.smoothLines ; m_borderColorSupport = true && s_extension[Extension::EXT_custom_border_color].m_supported && customBorderColorFeatures.customBorderColors ; m_timerQuerySupport = m_deviceProperties.limits.timestampComputeAndGraphics; const bool indirectDrawSupport = true && m_deviceFeatures.multiDrawIndirect && m_deviceFeatures.drawIndirectFirstInstance ; g_caps.supported |= ( 0 | BGFX_CAPS_ALPHA_TO_COVERAGE | (m_deviceFeatures.independentBlend ? BGFX_CAPS_BLEND_INDEPENDENT : 0) | BGFX_CAPS_COMPUTE | (indirectDrawSupport ? BGFX_CAPS_DRAW_INDIRECT : 0) | BGFX_CAPS_FRAGMENT_DEPTH | BGFX_CAPS_IMAGE_RW | (m_deviceFeatures.fullDrawIndexUint32 ? BGFX_CAPS_INDEX32 : 0) | BGFX_CAPS_INSTANCING | BGFX_CAPS_OCCLUSION_QUERY | (!headless ? BGFX_CAPS_SWAP_CHAIN : 0) | BGFX_CAPS_TEXTURE_2D_ARRAY | BGFX_CAPS_TEXTURE_3D | BGFX_CAPS_TEXTURE_BLIT | BGFX_CAPS_TEXTURE_COMPARE_ALL | (m_deviceFeatures.imageCubeArray ? BGFX_CAPS_TEXTURE_CUBE_ARRAY : 0) | BGFX_CAPS_TEXTURE_READ_BACK | BGFX_CAPS_VERTEX_ATTRIB_HALF | BGFX_CAPS_VERTEX_ATTRIB_UINT10 | BGFX_CAPS_VERTEX_ID ); g_caps.supported |= 0 | (s_extension[Extension::EXT_conservative_rasterization ].m_supported ? BGFX_CAPS_CONSERVATIVE_RASTER : 0) | (s_extension[Extension::EXT_shader_viewport_index_layer].m_supported ? BGFX_CAPS_VIEWPORT_LAYER_ARRAY : 0) | (s_extension[Extension::KHR_draw_indirect_count ].m_supported && indirectDrawSupport ? BGFX_CAPS_DRAW_INDIRECT_COUNT : 0) ; const uint32_t maxAttachments = bx::min<uint32_t>(m_deviceProperties.limits.maxFragmentOutputAttachments, m_deviceProperties.limits.maxColorAttachments); g_caps.limits.maxTextureSize = m_deviceProperties.limits.maxImageDimension2D; g_caps.limits.maxTextureLayers = m_deviceProperties.limits.maxImageArrayLayers; g_caps.limits.maxFBAttachments = bx::min<uint32_t>(maxAttachments, BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS); g_caps.limits.maxTextureSamplers = bx::min<uint32_t>(m_deviceProperties.limits.maxPerStageResources, BGFX_CONFIG_MAX_TEXTURE_SAMPLERS); g_caps.limits.maxComputeBindings = bx::min<uint32_t>(m_deviceProperties.limits.maxPerStageResources, BGFX_MAX_COMPUTE_BINDINGS); g_caps.limits.maxVertexStreams = bx::min<uint32_t>(m_deviceProperties.limits.maxVertexInputBindings, BGFX_CONFIG_MAX_VERTEX_STREAMS); { const VkSampleCountFlags sampleMask = ~0 & m_deviceProperties.limits.framebufferColorSampleCounts & m_deviceProperties.limits.framebufferDepthSampleCounts ; for (uint16_t ii = 0, last = 0; ii < BX_COUNTOF(s_msaa); ii++) { const VkSampleCountFlags sampleBit = s_msaa[ii].Sample; if (sampleBit & sampleMask) { last = ii; } else { s_msaa[ii] = s_msaa[last]; } } } { struct ImageTest { VkImageType type; VkImageUsageFlags usage; VkImageCreateFlags flags; uint32_t formatCaps[2]; }; const ImageTest imageTest[] = { { VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_SAMPLED_BIT, 0, { BGFX_CAPS_FORMAT_TEXTURE_2D, BGFX_CAPS_FORMAT_TEXTURE_2D_SRGB } }, { VK_IMAGE_TYPE_3D, VK_IMAGE_USAGE_SAMPLED_BIT, 0, { BGFX_CAPS_FORMAT_TEXTURE_3D, BGFX_CAPS_FORMAT_TEXTURE_3D_SRGB } }, { VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, { BGFX_CAPS_FORMAT_TEXTURE_CUBE, BGFX_CAPS_FORMAT_TEXTURE_CUBE_SRGB } }, { VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 0, { BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER, 0 } }, { VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0, { BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER, 0 } }, { VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_STORAGE_BIT, 0, { BGFX_CAPS_FORMAT_TEXTURE_IMAGE_READ, 0 } }, { VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_STORAGE_BIT, 0, { BGFX_CAPS_FORMAT_TEXTURE_IMAGE_WRITE, 0 } }, }; for (uint32_t ii = 0; ii < TextureFormat::Count; ++ii) { uint16_t support = BGFX_CAPS_FORMAT_TEXTURE_NONE; const bool depth = bimg::isDepth(bimg::TextureFormat::Enum(ii) ); VkFormat fmt = depth ? s_textureFormat[ii].m_fmtDsv : s_textureFormat[ii].m_fmt ; for (uint32_t jj = 0, num = depth ? 1 : 2; jj < num; ++jj) { if (VK_FORMAT_UNDEFINED != fmt) { for (uint32_t test = 0; test < BX_COUNTOF(imageTest); ++test) { const ImageTest& it = imageTest[test]; VkImageFormatProperties ifp; result = vkGetPhysicalDeviceImageFormatProperties(m_physicalDevice , fmt , it.type , VK_IMAGE_TILING_OPTIMAL , it.usage , it.flags , &ifp ); if (VK_SUCCESS == result) { support |= it.formatCaps[jj]; const bool multisample = VK_SAMPLE_COUNT_1_BIT < ifp.sampleCounts; if (it.usage & VK_IMAGE_USAGE_SAMPLED_BIT) { support |= 0 | BGFX_CAPS_FORMAT_TEXTURE_VERTEX | (multisample ? BGFX_CAPS_FORMAT_TEXTURE_MSAA : 0) ; } if (it.usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ) { support |= 0 | BGFX_CAPS_FORMAT_TEXTURE_MIP_AUTOGEN | (multisample ? BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA : 0) ; } } } } fmt = s_textureFormat[ii].m_fmtSrgb; } g_caps.formats[ii] = support; } } vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_memoryProperties); } { BX_TRACE("---"); uint32_t queueFamilyPropertyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties( m_physicalDevice , &queueFamilyPropertyCount , NULL ); VkQueueFamilyProperties* queueFamilyPropertices = (VkQueueFamilyProperties*)BX_ALLOC(g_allocator, queueFamilyPropertyCount * sizeof(VkQueueFamilyProperties) ); vkGetPhysicalDeviceQueueFamilyProperties( m_physicalDevice , &queueFamilyPropertyCount , queueFamilyPropertices ); for (uint32_t ii = 0; ii < queueFamilyPropertyCount; ++ii) { const VkQueueFamilyProperties& qfp = queueFamilyPropertices[ii]; BX_TRACE("Queue family property %d:", ii); BX_TRACE("\t Queue flags: 0x%08x", qfp.queueFlags); BX_TRACE("\t Queue count: %d", qfp.queueCount); BX_TRACE("\tTS valid bits: 0x%08x", qfp.timestampValidBits); BX_TRACE("\t Min image: %d x %d x %d" , qfp.minImageTransferGranularity.width , qfp.minImageTransferGranularity.height , qfp.minImageTransferGranularity.depth ); constexpr VkQueueFlags requiredFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; if (UINT32_MAX == m_globalQueueFamily && requiredFlags == (requiredFlags & qfp.queueFlags) ) { m_globalQueueFamily = ii; } } BX_FREE(g_allocator, queueFamilyPropertices); if (UINT32_MAX == m_globalQueueFamily) { BX_TRACE("Init error: Unable to find combined graphics and compute queue."); goto error; } } { uint32_t numEnabledLayers = 0; const char* enabledLayer[Layer::Count]; BX_TRACE("Enabled device layers:"); for (uint32_t ii = 0; ii < Layer::Count; ++ii) { const Layer& layer = s_layer[ii]; if (layer.m_device.m_supported && layer.m_device.m_initialize) { enabledLayer[numEnabledLayers++] = layer.m_name; BX_TRACE("\t%s", layer.m_name); } } uint32_t numEnabledExtensions = headless ? 1 : 2; const char* enabledExtension[Extension::Count + 2] = { VK_KHR_MAINTENANCE1_EXTENSION_NAME, VK_KHR_SWAPCHAIN_EXTENSION_NAME }; for (uint32_t ii = 0; ii < Extension::Count; ++ii) { const Extension& extension = s_extension[ii]; bool layerEnabled = extension.m_layer == Layer::Count || (s_layer[extension.m_layer].m_device.m_supported && s_layer[extension.m_layer].m_device.m_initialize); if (extension.m_supported && extension.m_initialize && !extension.m_instanceExt && layerEnabled) { enabledExtension[numEnabledExtensions++] = extension.m_name; } } BX_TRACE("Enabled device extensions:"); for (uint32_t ii = 0; ii < numEnabledExtensions; ++ii) { BX_TRACE("\t%s", enabledExtension[ii]); } float queuePriorities[1] = { 0.0f }; VkDeviceQueueCreateInfo dcqi; dcqi.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; dcqi.pNext = NULL; dcqi.flags = 0; dcqi.queueFamilyIndex = m_globalQueueFamily; dcqi.queueCount = 1; dcqi.pQueuePriorities = queuePriorities; VkDeviceCreateInfo dci; dci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; dci.pNext = nextFeatures; dci.flags = 0; dci.queueCreateInfoCount = 1; dci.pQueueCreateInfos = &dcqi; dci.enabledLayerCount = numEnabledLayers; dci.ppEnabledLayerNames = enabledLayer; dci.enabledExtensionCount = numEnabledExtensions; dci.ppEnabledExtensionNames = enabledExtension; dci.pEnabledFeatures = &m_deviceFeatures; result = vkCreateDevice( m_physicalDevice , &dci , m_allocatorCb , &m_device ); if (VK_SUCCESS != result) { BX_TRACE("Init error: vkCreateDevice failed %d: %s.", result, getName(result) ); goto error; } } errorState = ErrorState::DeviceCreated; BX_TRACE("Device functions:"); #define VK_IMPORT_DEVICE_FUNC(_optional, _func) \ _func = (PFN_##_func)vkGetDeviceProcAddr(m_device, #_func); \ BX_TRACE("\t%p " #_func, _func); \ imported &= _optional || NULL != _func VK_IMPORT_DEVICE #undef VK_IMPORT_DEVICE_FUNC if (!imported) { BX_TRACE("Init error: Failed to load device functions."); goto error; } vkGetDeviceQueue(m_device, m_globalQueueFamily, 0, &m_globalQueue); { m_numFramesInFlight = _init.resolution.maxFrameLatency == 0 ? BGFX_CONFIG_MAX_FRAME_LATENCY : _init.resolution.maxFrameLatency ; result = m_cmd.init(m_globalQueueFamily, m_globalQueue, m_numFramesInFlight); if (VK_SUCCESS != result) { BX_TRACE("Init error: creating command queue failed %d: %s.", result, getName(result) ); goto error; } result = m_cmd.alloc(&m_commandBuffer); if (VK_SUCCESS != result) { BX_TRACE("Init error: allocating command buffer failed %d: %s.", result, getName(result) ); goto error; } } errorState = ErrorState::CommandQueueCreated; m_presentElapsed = 0; { m_resolution = _init.resolution; m_resolution.reset &= ~BGFX_RESET_INTERNAL_FORCE; m_numWindows = 0; if (!headless) { m_textVideoMem.resize(false, _init.resolution.width, _init.resolution.height); m_textVideoMem.clear(); for (uint8_t ii = 0; ii < BX_COUNTOF(m_swapchainFormats); ++ii) { m_swapchainFormats[ii] = TextureFormat::Enum(ii); } result = m_backBuffer.create(UINT16_MAX, g_platformData.nwh, m_resolution.width, m_resolution.height, m_resolution.format); if (VK_SUCCESS != result) { BX_TRACE("Init error: creating swap chain failed %d: %s.", result, getName(result) ); goto error; } m_windows[0] = BGFX_INVALID_HANDLE; m_numWindows++; postReset(); } } errorState = ErrorState::SwapChainCreated; { VkDescriptorPoolSize dps[] = { { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, MAX_DESCRIPTOR_SETS * BGFX_CONFIG_MAX_TEXTURE_SAMPLERS }, { VK_DESCRIPTOR_TYPE_SAMPLER, MAX_DESCRIPTOR_SETS * BGFX_CONFIG_MAX_TEXTURE_SAMPLERS }, { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, MAX_DESCRIPTOR_SETS * 2 }, { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, MAX_DESCRIPTOR_SETS * BGFX_CONFIG_MAX_TEXTURE_SAMPLERS }, { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, MAX_DESCRIPTOR_SETS * BGFX_CONFIG_MAX_TEXTURE_SAMPLERS }, }; VkDescriptorPoolCreateInfo dpci; dpci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; dpci.pNext = NULL; dpci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; dpci.maxSets = MAX_DESCRIPTOR_SETS; dpci.poolSizeCount = BX_COUNTOF(dps); dpci.pPoolSizes = dps; result = vkCreateDescriptorPool(m_device, &dpci, m_allocatorCb, &m_descriptorPool); if (VK_SUCCESS != result) { BX_TRACE("Init error: vkCreateDescriptorPool failed %d: %s.", result, getName(result) ); goto error; } VkPipelineCacheCreateInfo pcci; pcci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; pcci.pNext = NULL; pcci.flags = 0; pcci.initialDataSize = 0; pcci.pInitialData = NULL; result = vkCreatePipelineCache(m_device, &pcci, m_allocatorCb, &m_pipelineCache); if (VK_SUCCESS != result) { BX_TRACE("Init error: vkCreatePipelineCache failed %d: %s.", result, getName(result) ); goto error; } } { const uint32_t size = 128; const uint32_t count = BGFX_CONFIG_MAX_DRAW_CALLS; for (uint32_t ii = 0; ii < m_numFramesInFlight; ++ii) { BX_TRACE("Create scratch buffer %d", ii); m_scratchBuffer[ii].create(size, count); } } errorState = ErrorState::DescriptorCreated; if (NULL == vkSetDebugUtilsObjectNameEXT) { vkSetDebugUtilsObjectNameEXT = stubSetDebugUtilsObjectNameEXT; } if (NULL == vkCmdBeginDebugUtilsLabelEXT || NULL == vkCmdEndDebugUtilsLabelEXT) { vkCmdBeginDebugUtilsLabelEXT = stubCmdBeginDebugUtilsLabelEXT; vkCmdEndDebugUtilsLabelEXT = stubCmdEndDebugUtilsLabelEXT; } if (NULL == vkCmdInsertDebugUtilsLabelEXT) { vkCmdInsertDebugUtilsLabelEXT = stubCmdInsertDebugUtilsLabelEXT; } // Init reserved part of view name. for (uint32_t ii = 0; ii < BGFX_CONFIG_MAX_VIEWS; ++ii) { bx::snprintf(s_viewName[ii], BGFX_CONFIG_MAX_VIEW_NAME_RESERVED+1, "%3d ", ii); } if (m_timerQuerySupport) { result = m_gpuTimer.init(); if (VK_SUCCESS != result) { BX_TRACE("Init error: creating GPU timer failed %d: %s.", result, getName(result) ); goto error; } } errorState = ErrorState::TimerQueryCreated; result = m_occlusionQuery.init(); if (VK_SUCCESS != result) { BX_TRACE("Init error: creating occlusion query failed %d: %s.", result, getName(result) ); goto error; } g_internalData.context = m_device; return true; error: BX_TRACE("errorState %d", errorState); switch (errorState) { case ErrorState::TimerQueryCreated: if (m_timerQuerySupport) { m_gpuTimer.shutdown(); } BX_FALLTHROUGH; case ErrorState::DescriptorCreated: for (uint32_t ii = 0; ii < m_numFramesInFlight; ++ii) { m_scratchBuffer[ii].destroy(); } vkDestroy(m_pipelineCache); vkDestroy(m_descriptorPool); BX_FALLTHROUGH; case ErrorState::SwapChainCreated: m_backBuffer.destroy(); BX_FALLTHROUGH; case ErrorState::CommandQueueCreated: m_cmd.shutdown(); BX_FALLTHROUGH; case ErrorState::DeviceCreated: vkDestroyDevice(m_device, m_allocatorCb); BX_FALLTHROUGH; case ErrorState::InstanceCreated: if (VK_NULL_HANDLE != m_debugReportCallback) { vkDestroyDebugReportCallbackEXT(m_instance, m_debugReportCallback, m_allocatorCb); } vkDestroyInstance(m_instance, m_allocatorCb); BX_FALLTHROUGH; case ErrorState::LoadedVulkan1: bx::dlclose(m_vulkan1Dll); m_vulkan1Dll = NULL; m_allocatorCb = NULL; unloadRenderDoc(m_renderDocDll); BX_FALLTHROUGH; case ErrorState::Default: break; }; return false; } void shutdown() { VK_CHECK(vkDeviceWaitIdle(m_device) ); if (m_timerQuerySupport) { m_gpuTimer.shutdown(); } m_occlusionQuery.shutdown(); preReset(); m_pipelineStateCache.invalidate(); m_descriptorSetLayoutCache.invalidate(); m_renderPassCache.invalidate(); m_samplerCache.invalidate(); m_samplerBorderColorCache.invalidate(); m_imageViewCache.invalidate(); for (uint32_t ii = 0; ii < m_numFramesInFlight; ++ii) { m_scratchBuffer[ii].destroy(); } for (uint32_t ii = 0; ii < BX_COUNTOF(m_frameBuffers); ++ii) { m_frameBuffers[ii].destroy(); } for (uint32_t ii = 0; ii < BX_COUNTOF(m_indexBuffers); ++ii) { m_indexBuffers[ii].destroy(); } for (uint32_t ii = 0; ii < BX_COUNTOF(m_vertexBuffers); ++ii) { m_vertexBuffers[ii].destroy(); } for (uint32_t ii = 0; ii < BX_COUNTOF(m_shaders); ++ii) { m_shaders[ii].destroy(); } for (uint32_t ii = 0; ii < BX_COUNTOF(m_textures); ++ii) { m_textures[ii].destroy(); } m_backBuffer.destroy(); m_cmd.shutdown(); vkDestroy(m_pipelineCache); vkDestroy(m_descriptorPool); vkDestroyDevice(m_device, m_allocatorCb); if (VK_NULL_HANDLE != m_debugReportCallback) { vkDestroyDebugReportCallbackEXT(m_instance, m_debugReportCallback, m_allocatorCb); } vkDestroyInstance(m_instance, m_allocatorCb); bx::dlclose(m_vulkan1Dll); m_vulkan1Dll = NULL; m_allocatorCb = NULL; unloadRenderDoc(m_renderDocDll); } RendererType::Enum getRendererType() const override { return RendererType::Vulkan; } const char* getRendererName() const override { return BGFX_RENDERER_VULKAN_NAME; } bool isDeviceRemoved() override { return false; } void flip() override { int64_t start = bx::getHPCounter(); for (uint16_t ii = 0; ii < m_numWindows; ++ii) { FrameBufferVK& fb = isValid(m_windows[ii]) ? m_frameBuffers[m_windows[ii].idx] : m_backBuffer ; fb.present(); } int64_t now = bx::getHPCounter(); m_presentElapsed += now - start; } void createIndexBuffer(IndexBufferHandle _handle, const Memory* _mem, uint16_t _flags) override { m_indexBuffers[_handle.idx].create(m_commandBuffer, _mem->size, _mem->data, _flags, false); } void destroyIndexBuffer(IndexBufferHandle _handle) override { m_indexBuffers[_handle.idx].destroy(); } void createVertexLayout(VertexLayoutHandle _handle, const VertexLayout& _layout) override { VertexLayout& layout = m_vertexLayouts[_handle.idx]; bx::memCopy(&layout, &_layout, sizeof(VertexLayout) ); dump(layout); } void destroyVertexLayout(VertexLayoutHandle /*_handle*/) override { } void createVertexBuffer(VertexBufferHandle _handle, const Memory* _mem, VertexLayoutHandle _layoutHandle, uint16_t _flags) override { m_vertexBuffers[_handle.idx].create(m_commandBuffer, _mem->size, _mem->data, _layoutHandle, _flags); } void destroyVertexBuffer(VertexBufferHandle _handle) override { m_vertexBuffers[_handle.idx].destroy(); } void createDynamicIndexBuffer(IndexBufferHandle _handle, uint32_t _size, uint16_t _flags) override { m_indexBuffers[_handle.idx].create(m_commandBuffer, _size, NULL, _flags, false); } void updateDynamicIndexBuffer(IndexBufferHandle _handle, uint32_t _offset, uint32_t _size, const Memory* _mem) override { m_indexBuffers[_handle.idx].update(m_commandBuffer, _offset, bx::min<uint32_t>(_size, _mem->size), _mem->data); } void destroyDynamicIndexBuffer(IndexBufferHandle _handle) override { m_indexBuffers[_handle.idx].destroy(); } void createDynamicVertexBuffer(VertexBufferHandle _handle, uint32_t _size, uint16_t _flags) override { VertexLayoutHandle layoutHandle = BGFX_INVALID_HANDLE; m_vertexBuffers[_handle.idx].create(m_commandBuffer, _size, NULL, layoutHandle, _flags); } void updateDynamicVertexBuffer(VertexBufferHandle _handle, uint32_t _offset, uint32_t _size, const Memory* _mem) override { m_vertexBuffers[_handle.idx].update(m_commandBuffer, _offset, bx::min<uint32_t>(_size, _mem->size), _mem->data); } void destroyDynamicVertexBuffer(VertexBufferHandle _handle) override { m_vertexBuffers[_handle.idx].destroy(); } void createShader(ShaderHandle _handle, const Memory* _mem) override { m_shaders[_handle.idx].create(_mem); } void destroyShader(ShaderHandle _handle) override { m_shaders[_handle.idx].destroy(); } void createProgram(ProgramHandle _handle, ShaderHandle _vsh, ShaderHandle _fsh) override { m_program[_handle.idx].create(&m_shaders[_vsh.idx], isValid(_fsh) ? &m_shaders[_fsh.idx] : NULL); } void destroyProgram(ProgramHandle _handle) override { m_program[_handle.idx].destroy(); } void* createTexture(TextureHandle _handle, const Memory* _mem, uint64_t _flags, uint8_t _skip) override { return m_textures[_handle.idx].create(m_commandBuffer, _mem, _flags, _skip); } void updateTextureBegin(TextureHandle /*_handle*/, uint8_t /*_side*/, uint8_t /*_mip*/) override { } void updateTexture(TextureHandle _handle, uint8_t _side, uint8_t _mip, const Rect& _rect, uint16_t _z, uint16_t _depth, uint16_t _pitch, const Memory* _mem) override { m_textures[_handle.idx].update(m_commandBuffer, _side, _mip, _rect, _z, _depth, _pitch, _mem); } void updateTextureEnd() override { } void readTexture(TextureHandle _handle, void* _data, uint8_t _mip) override { TextureVK& texture = m_textures[_handle.idx]; uint32_t height = bx::uint32_max(1, texture.m_height >> _mip); uint32_t pitch = texture.m_readback.pitch(_mip); uint32_t size = height * pitch; VkDeviceMemory stagingMemory; VkBuffer stagingBuffer; VK_CHECK(createReadbackBuffer(size, &stagingBuffer, &stagingMemory) ); texture.m_readback.copyImageToBuffer( m_commandBuffer , stagingBuffer , texture.m_currentImageLayout , texture.m_aspectMask , _mip ); kick(true); texture.m_readback.readback(stagingMemory, 0, _data, _mip); vkDestroy(stagingBuffer); vkDestroy(stagingMemory); } void resizeTexture(TextureHandle _handle, uint16_t _width, uint16_t _height, uint8_t _numMips, uint16_t _numLayers) override { const TextureVK& texture = m_textures[_handle.idx]; const TextureFormat::Enum format = TextureFormat::Enum(texture.m_requestedFormat); const uint64_t flags = texture.m_flags; const uint32_t size = sizeof(uint32_t) + sizeof(TextureCreate); const Memory* mem = alloc(size); bx::StaticMemoryBlockWriter writer(mem->data, mem->size); uint32_t magic = BGFX_CHUNK_MAGIC_TEX; bx::write(&writer, magic, bx::ErrorAssert{}); TextureCreate tc; tc.m_width = _width; tc.m_height = _height; tc.m_depth = 0; tc.m_numLayers = _numLayers; tc.m_numMips = _numMips; tc.m_format = format; tc.m_cubeMap = false; tc.m_mem = NULL; bx::write(&writer, tc, bx::ErrorAssert{}); destroyTexture(_handle); createTexture(_handle, mem, flags, 0); bgfx::release(mem); } void overrideInternal(TextureHandle /*_handle*/, uintptr_t /*_ptr*/) override { } uintptr_t getInternal(TextureHandle /*_handle*/) override { return 0; } void destroyTexture(TextureHandle _handle) override { m_imageViewCache.invalidateWithParent(_handle.idx); m_textures[_handle.idx].destroy(); } void createFrameBuffer(FrameBufferHandle _handle, uint8_t _num, const Attachment* _attachment) override { m_frameBuffers[_handle.idx].create(_num, _attachment); } void createFrameBuffer(FrameBufferHandle _handle, void* _nwh, uint32_t _width, uint32_t _height, TextureFormat::Enum _format, TextureFormat::Enum _depthFormat) override { for (uint32_t ii = 0, num = m_numWindows; ii < num; ++ii) { FrameBufferHandle handle = m_windows[ii]; if (isValid(handle) && m_frameBuffers[handle.idx].m_nwh == _nwh) { destroyFrameBuffer(handle); } } uint16_t denseIdx = m_numWindows++; m_windows[denseIdx] = _handle; VK_CHECK(m_frameBuffers[_handle.idx].create(denseIdx, _nwh, _width, _height, _format, _depthFormat) ); } void destroyFrameBuffer(FrameBufferHandle _handle) override { FrameBufferVK& frameBuffer = m_frameBuffers[_handle.idx]; if (_handle.idx == m_fbh.idx) { setFrameBuffer(BGFX_INVALID_HANDLE, false); } uint16_t denseIdx = frameBuffer.destroy(); if (UINT16_MAX != denseIdx) { --m_numWindows; if (m_numWindows > 1) { FrameBufferHandle handle = m_windows[m_numWindows]; m_windows[m_numWindows] = {kInvalidHandle}; if (m_numWindows != denseIdx) { m_windows[denseIdx] = handle; m_frameBuffers[handle.idx].m_denseIdx = denseIdx; } } } } void createUniform(UniformHandle _handle, UniformType::Enum _type, uint16_t _num, const char* _name) override { if (NULL != m_uniforms[_handle.idx]) { BX_FREE(g_allocator, m_uniforms[_handle.idx]); } const uint32_t size = bx::alignUp(g_uniformTypeSize[_type] * _num, 16); void* data = BX_ALLOC(g_allocator, size); bx::memSet(data, 0, size); m_uniforms[_handle.idx] = data; m_uniformReg.add(_handle, _name); } void destroyUniform(UniformHandle _handle) override { BX_FREE(g_allocator, m_uniforms[_handle.idx]); m_uniforms[_handle.idx] = NULL; } void requestScreenShot(FrameBufferHandle _fbh, const char* _filePath) override { const FrameBufferVK& frameBuffer = isValid(_fbh) ? m_frameBuffers[_fbh.idx] : m_backBuffer ; const SwapChainVK& swapChain = frameBuffer.m_swapChain; if (!isSwapChainReadable(swapChain) ) { BX_TRACE("Unable to capture screenshot %s.", _filePath); return; } auto callback = [](void* _src, uint32_t _width, uint32_t _height, uint32_t _pitch, const void* _userData) { const char* filePath = (const char*)_userData; g_callback->screenShot( filePath , _width , _height , _pitch , _src , _height * _pitch , false ); }; const uint8_t bpp = bimg::getBitsPerPixel(bimg::TextureFormat::Enum(swapChain.m_colorFormat) ); const uint32_t size = frameBuffer.m_width * frameBuffer.m_height * bpp / 8; VkDeviceMemory stagingMemory; VkBuffer stagingBuffer; VK_CHECK(createReadbackBuffer(size, &stagingBuffer, &stagingMemory) ); readSwapChain(swapChain, stagingBuffer, stagingMemory, callback, _filePath); vkDestroy(stagingBuffer); vkDestroy(stagingMemory); } void updateViewName(ViewId _id, const char* _name) override { bx::strCopy(&s_viewName[_id][BGFX_CONFIG_MAX_VIEW_NAME_RESERVED] , BX_COUNTOF(s_viewName[0]) - BGFX_CONFIG_MAX_VIEW_NAME_RESERVED , _name ); } void updateUniform(uint16_t _loc, const void* _data, uint32_t _size) override { bx::memCopy(m_uniforms[_loc], _data, _size); } void invalidateOcclusionQuery(OcclusionQueryHandle _handle) override { m_occlusionQuery.invalidate(_handle); } void setMarker(const char* _marker, uint16_t _len) override { if (BX_ENABLED(BGFX_CONFIG_DEBUG_ANNOTATION) ) { BX_UNUSED(_len); const uint32_t abgr = kColorMarker; VkDebugUtilsLabelEXT dul; dul.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; dul.pNext = NULL; dul.pLabelName = _marker; dul.color[0] = ((abgr >> 24) & 0xff) / 255.0f; dul.color[1] = ((abgr >> 16) & 0xff) / 255.0f; dul.color[2] = ((abgr >> 8) & 0xff) / 255.0f; dul.color[3] = ((abgr >> 0) & 0xff) / 255.0f; vkCmdInsertDebugUtilsLabelEXT(m_commandBuffer, &dul); } } virtual void setName(Handle _handle, const char* _name, uint16_t _len) override { switch (_handle.type) { case Handle::IndexBuffer: setDebugObjectName(m_device, m_indexBuffers[_handle.idx].m_buffer, "%.*s", _len, _name); break; case Handle::Shader: setDebugObjectName(m_device, m_shaders[_handle.idx].m_module, "%.*s", _len, _name); break; case Handle::Texture: setDebugObjectName(m_device, m_textures[_handle.idx].m_textureImage, "%.*s", _len, _name); if (VK_NULL_HANDLE != m_textures[_handle.idx].m_singleMsaaImage) { setDebugObjectName(m_device, m_textures[_handle.idx].m_singleMsaaImage, "%.*s", _len, _name); } break; case Handle::VertexBuffer: setDebugObjectName(m_device, m_vertexBuffers[_handle.idx].m_buffer, "%.*s", _len, _name); break; default: BX_ASSERT(false, "Invalid handle type?! %d", _handle.type); break; } } template<typename Ty> void release(Ty& _object) { if (VK_NULL_HANDLE != _object) { m_cmd.release(uint64_t(_object.vk), getType<Ty>() ); _object = VK_NULL_HANDLE; } } void submitBlit(BlitState& _bs, uint16_t _view); void submit(Frame* _render, ClearQuad& _clearQuad, TextVideoMemBlitter& _textVideoMemBlitter) override; void blitSetup(TextVideoMemBlitter& _blitter) override { const uint32_t width = m_backBuffer.m_width; const uint32_t height = m_backBuffer.m_height; setFrameBuffer(BGFX_INVALID_HANDLE); VkViewport vp; vp.x = 0.0f; vp.y = float(height); vp.width = float(width); vp.height = -float(height); vp.minDepth = 0.0f; vp.maxDepth = 1.0f; vkCmdSetViewport(m_commandBuffer, 0, 1, &vp); VkRect2D rc; rc.offset.x = 0; rc.offset.y = 0; rc.extent.width = width; rc.extent.height = height; vkCmdSetScissor(m_commandBuffer, 0, 1, &rc); const uint64_t state = 0 | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_DEPTH_TEST_ALWAYS | BGFX_STATE_MSAA ; const VertexLayout* layout = &m_vertexLayouts[_blitter.m_vb->layoutHandle.idx]; VkPipeline pso = getPipeline(state , packStencil(BGFX_STENCIL_DEFAULT, BGFX_STENCIL_DEFAULT) , 1 , &layout , _blitter.m_program , 0 ); vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pso); ProgramVK& program = m_program[_blitter.m_program.idx]; float proj[16]; bx::mtxOrtho(proj, 0.0f, (float)width, (float)height, 0.0f, 0.0f, 1000.0f, 0.0f, false); PredefinedUniform& predefined = m_program[_blitter.m_program.idx].m_predefined[0]; uint8_t flags = predefined.m_type; setShaderUniform(flags, predefined.m_loc, proj, 4); UniformBuffer* vcb = program.m_vsh->m_constantBuffer; if (NULL != vcb) { commit(*vcb); } ScratchBufferVK& scratchBuffer = m_scratchBuffer[m_cmd.m_currentFrameInFlight]; const uint32_t bufferOffset = scratchBuffer.write(m_vsScratch, program.m_vsh->m_size); const TextureVK& texture = m_textures[_blitter.m_texture.idx]; RenderBind bind; bind.clear(); bind.m_bind[0].m_type = Binding::Texture; bind.m_bind[0].m_idx = _blitter.m_texture.idx; bind.m_bind[0].m_samplerFlags = (uint32_t)(texture.m_flags & BGFX_SAMPLER_BITS_MASK); const VkDescriptorSet descriptorSet = getDescriptorSet(program, bind, scratchBuffer, NULL); vkCmdBindDescriptorSets( m_commandBuffer , VK_PIPELINE_BIND_POINT_GRAPHICS , program.m_pipelineLayout , 0 , 1 , &descriptorSet , 1 , &bufferOffset ); const VertexBufferVK& vb = m_vertexBuffers[_blitter.m_vb->handle.idx]; const VkDeviceSize offset = 0; vkCmdBindVertexBuffers(m_commandBuffer, 0, 1, &vb.m_buffer, &offset); const BufferVK& ib = m_indexBuffers[_blitter.m_ib->handle.idx]; vkCmdBindIndexBuffer( m_commandBuffer , ib.m_buffer , 0 , VK_INDEX_TYPE_UINT16 ); } void blitRender(TextVideoMemBlitter& _blitter, uint32_t _numIndices) override { const uint32_t numVertices = _numIndices*4/6; if (0 < numVertices && m_backBuffer.isRenderable() ) { m_indexBuffers[_blitter.m_ib->handle.idx].update(m_commandBuffer, 0, _numIndices*2, _blitter.m_ib->data); m_vertexBuffers[_blitter.m_vb->handle.idx].update(m_commandBuffer, 0, numVertices*_blitter.m_layout.m_stride, _blitter.m_vb->data, true); VkRenderPassBeginInfo rpbi; rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rpbi.pNext = NULL; rpbi.renderPass = m_backBuffer.m_renderPass; rpbi.framebuffer = m_backBuffer.m_currentFramebuffer; rpbi.renderArea.offset.x = 0; rpbi.renderArea.offset.y = 0; rpbi.renderArea.extent.width = m_backBuffer.m_width; rpbi.renderArea.extent.height = m_backBuffer.m_height; rpbi.clearValueCount = 0; rpbi.pClearValues = NULL; vkCmdBeginRenderPass(m_commandBuffer, &rpbi, VK_SUBPASS_CONTENTS_INLINE); vkCmdDrawIndexed(m_commandBuffer, _numIndices, 1, 0, 0, 0); vkCmdEndRenderPass(m_commandBuffer); } } void preReset() { for (uint32_t ii = 0; ii < BX_COUNTOF(m_frameBuffers); ++ii) { m_frameBuffers[ii].preReset(); } if (m_captureSize > 0) { g_callback->captureEnd(); release(m_captureBuffer); release(m_captureMemory); m_captureSize = 0; } } void postReset() { for (uint32_t ii = 0; ii < BX_COUNTOF(m_frameBuffers); ++ii) { m_frameBuffers[ii].postReset(); } if (m_resolution.reset & BGFX_RESET_CAPTURE) { const uint8_t bpp = bimg::getBitsPerPixel(bimg::TextureFormat::Enum(m_backBuffer.m_swapChain.m_colorFormat) ); const uint32_t captureSize = m_backBuffer.m_width * m_backBuffer.m_height * bpp / 8; const uint8_t dstBpp = bimg::getBitsPerPixel(bimg::TextureFormat::BGRA8); const uint32_t dstPitch = m_backBuffer.m_width * dstBpp / 8; if (captureSize > m_captureSize) { release(m_captureBuffer); release(m_captureMemory); m_captureSize = captureSize; VK_CHECK(createReadbackBuffer(m_captureSize, &m_captureBuffer, &m_captureMemory) ); } g_callback->captureBegin(m_resolution.width, m_resolution.height, dstPitch, TextureFormat::BGRA8, false); } } bool updateResolution(const Resolution& _resolution) { const bool suspended = !!(_resolution.reset & BGFX_RESET_SUSPEND); float maxAnisotropy = 1.0f; if (!!(_resolution.reset & BGFX_RESET_MAXANISOTROPY) ) { maxAnisotropy = m_deviceProperties.limits.maxSamplerAnisotropy; } if (m_maxAnisotropy != maxAnisotropy) { m_maxAnisotropy = maxAnisotropy; m_samplerCache.invalidate(); m_samplerBorderColorCache.invalidate(); } bool depthClamp = m_deviceFeatures.depthClamp && !!(_resolution.reset & BGFX_RESET_DEPTH_CLAMP); if (m_depthClamp != depthClamp) { m_depthClamp = depthClamp; m_pipelineStateCache.invalidate(); } if (NULL == m_backBuffer.m_nwh) { return suspended; } uint32_t flags = _resolution.reset & ~(0 | BGFX_RESET_SUSPEND | BGFX_RESET_MAXANISOTROPY | BGFX_RESET_DEPTH_CLAMP ); // Note: m_needToRefreshSwapchain is deliberately ignored when deciding whether to recreate the swapchain // because it can happen several frames before submit is called with the new resolution. // Instead, vkAcquireNextImageKHR and all draws to the backbuffer are skipped until the window size is updated. // That also fixes a related issue where VK_ERROR_OUT_OF_DATE_KHR is returned from // vkQueuePresentKHR when the window doesn't exist anymore, and vkGetPhysicalDeviceSurfaceCapabilitiesKHR // fails with VK_ERROR_SURFACE_LOST_KHR. if (false || m_resolution.format != _resolution.format || m_resolution.width != _resolution.width || m_resolution.height != _resolution.height || m_resolution.reset != flags || m_backBuffer.m_swapChain.m_needToRecreateSurface) { flags &= ~BGFX_RESET_INTERNAL_FORCE; if (m_backBuffer.m_nwh != g_platformData.nwh) { m_backBuffer.m_nwh = g_platformData.nwh; } m_resolution = _resolution; m_resolution.reset = flags; m_textVideoMem.resize(false, _resolution.width, _resolution.height); m_textVideoMem.clear(); preReset(); m_backBuffer.update(m_commandBuffer, m_resolution); postReset(); } return suspended; } void setShaderUniform(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs) { if (_flags & kUniformFragmentBit) { bx::memCopy(&m_fsScratch[_regIndex], _val, _numRegs*16); } else { bx::memCopy(&m_vsScratch[_regIndex], _val, _numRegs*16); } } void setShaderUniform4f(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs) { setShaderUniform(_flags, _regIndex, _val, _numRegs); } void setShaderUniform4x4f(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs) { setShaderUniform(_flags, _regIndex, _val, _numRegs); } void setFrameBuffer(FrameBufferHandle _fbh, bool _acquire = true) { BX_ASSERT(false || isValid(_fbh) || NULL != m_backBuffer.m_nwh , "Rendering to backbuffer in headless mode." wds[2].pBufferInfo = NULL; wds[2].pTexelBufferView = NULL; m_vsChanges = 0; m_fsChanges = 0; vkUpdateDescriptorSets(m_device, 3, wds, 0, NULL); vkCmdBindDescriptorSets( m_commandBuffer FrameBufferVK& oldFrameBuffer = isValid(m_fbh) ? m_frameBuffers[m_fbh.idx] : m_backBuffer ; if (NULL == oldFrameBuffer.m_nwh && m_fbh.idx != _fbh.idx) { oldFrameBuffer.resolve(); for (uint8_t ii = 0, num = oldFrameBuffer.m_num; ii < num; ++ii) { TextureVK& texture = m_textures[oldFrameBuffer.m_texture[ii].idx]; texture.setImageMemoryBarrier(m_commandBuffer, texture.m_sampledLayout); if (VK_NULL_HANDLE != texture.m_singleMsaaImage) { texture.setImageMemoryBarrier(m_commandBuffer, texture.m_sampledLayout, true); } } if (isValid(oldFrameBuffer.m_depth) ) { TextureVK& texture = m_textures[oldFrameBuffer.m_depth.idx]; const bool writeOnly = 0 != (texture.m_flags&BGFX_TEXTURE_RT_WRITE_ONLY); if (!writeOnly) { texture.setImageMemoryBarrier(m_commandBuffer, texture.m_sampledLayout); } } } if (NULL == newFrameBuffer.m_nwh) { for (uint8_t ii = 0, num = newFrameBuffer.m_num; ii < num; ++ii) { TextureVK& texture = m_textures[newFrameBuffer.m_texture[ii].idx]; texture.setImageMemoryBarrier( m_commandBuffer , VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ); } if (isValid(newFrameBuffer.m_depth) ) { TextureVK& texture = m_textures[newFrameBuffer.m_depth.idx]; texture.setImageMemoryBarrier( m_commandBuffer , VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ); } newFrameBuffer.acquire(m_commandBuffer); } if (_acquire) { int64_t start = bx::getHPCounter(); newFrameBuffer.acquire(m_commandBuffer); int64_t now = bx::getHPCounter(); if (NULL != newFrameBuffer.m_nwh) { m_presentElapsed += now - start; } } m_fbh = _fbh; } void setDebugWireframe(bool _wireframe) { const bool wireframe = m_deviceFeatures.fillModeNonSolid && _wireframe; if (m_wireframe != wireframe) { m_wireframe = wireframe; m_pipelineStateCache.invalidate(); } } void setBlendState(VkPipelineColorBlendStateCreateInfo& _desc, uint64_t _state, uint32_t _rgba = 0) { VkPipelineColorBlendAttachmentState* bas = const_cast<VkPipelineColorBlendAttachmentState*>(_desc.pAttachments); uint8_t writeMask = 0; writeMask |= (_state & BGFX_STATE_WRITE_R) ? VK_COLOR_COMPONENT_R_BIT : 0; writeMask |= (_state & BGFX_STATE_WRITE_G) ? VK_COLOR_COMPONENT_G_BIT : 0; writeMask |= (_state & BGFX_STATE_WRITE_B) ? VK_COLOR_COMPONENT_B_BIT : 0; writeMask |= (_state & BGFX_STATE_WRITE_A) ? VK_COLOR_COMPONENT_A_BIT : 0; bas->blendEnable = !!(BGFX_STATE_BLEND_MASK & _state); { const uint32_t blend = uint32_t( (_state & BGFX_STATE_BLEND_MASK ) >> BGFX_STATE_BLEND_SHIFT); const uint32_t equation = uint32_t( (_state & BGFX_STATE_BLEND_EQUATION_MASK) >> BGFX_STATE_BLEND_EQUATION_SHIFT); const uint32_t srcRGB = (blend ) & 0xf; const uint32_t dstRGB = (blend >> 4) & 0xf; const uint32_t srcA = (blend >> 8) & 0xf; const uint32_t dstA = (blend >> 12) & 0xf; const uint32_t equRGB = (equation ) & 0x7; const uint32_t equA = (equation >> 3) & 0x7; bas->srcColorBlendFactor = s_blendFactor[srcRGB][0]; bas->dstColorBlendFactor = s_blendFactor[dstRGB][0]; bas->colorBlendOp = s_blendEquation[equRGB]; bas->srcAlphaBlendFactor = s_blendFactor[srcA][1]; bas->dstAlphaBlendFactor = s_blendFactor[dstA][1]; bas->alphaBlendOp = s_blendEquation[equA]; bas->colorWriteMask = writeMask; } const FrameBufferVK& frameBuffer = isValid(m_fbh) ? m_frameBuffers[m_fbh.idx] : m_backBuffer ; const uint32_t numAttachments = NULL == frameBuffer.m_nwh ? frameBuffer.m_num : 1 ; if (!!(BGFX_STATE_BLEND_INDEPENDENT & _state) && m_deviceFeatures.independentBlend ) {
{'commit': 'diff --git a/examples/15-shadowmaps-simple/fs_sms_shadow.sc b/examples/15-shadowmaps-simple/fs_sms_shadow.sc\nindex 3cdf1973f..ff26f3891 100644\n--- a/examples/15-shadowmaps-simple/fs_sms_shadow.sc\n+++ b/examples/15-shadowmaps-simple/fs_sms_shadow.sc\n@@ -7,5 +7,8 @@\n \n void main()\n {\n+\t// Vulkan does not like shader writing to a target that is not attached\n+#ifndef BGFX_SHADER_LANGUAGE_SPIRV\n \tgl_FragColor = vec4_splat(0.0);\n+#endif\n }\ndiff --git a/src/renderer_vk.cpp b/src/renderer_vk.cpp\nindex 997c749f2..d8a16ef46 100644\n--- a/src/renderer_vk.cpp\n+++ b/src/renderer_vk.cpp\n@@ -2804,9 +2804,6 @@ VK_IMPORT_DEVICE\n \t\t\twds[2].pBufferInfo = NULL;\n \t\t\twds[2].pTexelBufferView = NULL;\n \n-\t\t\tm_vsChanges = 0;\n-\t\t\tm_fsChanges = 0;\n-\n \t\t\tvkUpdateDescriptorSets(m_device, 3, wds, 0, NULL);\n \t\t\tvkCmdBindDescriptorSets(\n \t\t\t\tm_commandBuffer\n@@ -3013,16 +3010,13 @@ VK_IMPORT_DEVICE\n \n \t\tvoid setShaderUniform(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs)\n \t\t{\n-\t\t\tBX_UNUSED(_flags, _regIndex, _val, _numRegs);\n-\t\t\tif (_flags&BGFX_UNIFORM_FRAGMENTBIT)\n+\t\t\tif (_flags & BGFX_UNIFORM_FRAGMENTBIT)\n \t\t\t{\n \t\t\t\tbx::memCopy(&m_fsScratch[_regIndex], _val, _numRegs*16);\n-\t\t\t\tm_fsChanges += _numRegs;\n \t\t\t}\n \t\t\telse\n \t\t\t{\n \t\t\t\tbx::memCopy(&m_vsScratch[_regIndex], _val, _numRegs*16);\n-\t\t\t\tm_vsChanges += _numRegs;\n \t\t\t}\n \t\t}\n \n@@ -3070,9 +3064,6 @@ VK_IMPORT_DEVICE\n //\t\t\t\t\t, NULL\n //\t\t\t\t\t);\n //\t\t\t}\n-//\n-//\t\t\tm_vsChanges = 0;\n-//\t\t\tm_fsChanges = 0;\n //\t\t}\n \n \t\tvoid setFrameBuffer(FrameBufferHandle _fbh, bool _msaa = true)\n@@ -3604,7 +3595,10 @@ VK_IMPORT_DEVICE\n \t\t\tmurmur.add(_stencil);\n \t\t\tmurmur.add(program.m_vsh->m_hash);\n \t\t\tmurmur.add(program.m_vsh->m_attrMask, sizeof(program.m_vsh->m_attrMask) );\n-\t\t\tmurmur.add(program.m_fsh->m_hash);\n+\t\t\tif (NULL != program.m_fsh)\n+\t\t\t{\n+\t\t\t\tmurmur.add(program.m_fsh->m_hash);\n+\t\t\t}\n \t\t\tfor (uint8_t ii = 0; ii < _numStreams; ++ii)\n \t\t\t{\n \t\t\t\tmurmur.add(_layouts[ii]->m_hash);\n@@ -3670,13 +3664,17 @@ VK_IMPORT_DEVICE\n \t\t\tshaderStages[0].module = program.m_vsh->m_module;\n \t\t\tshaderStages[0].pName  = "main";\n \t\t\tshaderStages[0].pSpecializationInfo = NULL;\n-\t\t\tshaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n-\t\t\tshaderStages[1].pNext = NULL;\n-\t\t\tshaderStages[1].flags = 0;\n-\t\t\tshaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;\n-\t\t\tshaderStages[1].module = program.m_fsh->m_module;\n-\t\t\tshaderStages[1].pName  = "main";\n-\t\t\tshaderStages[1].pSpecializationInfo = NULL;\n+\n+\t\t\tif (NULL != program.m_fsh)\n+\t\t\t{\n+\t\t\t\tshaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n+\t\t\t\tshaderStages[1].pNext = NULL;\n+\t\t\t\tshaderStages[1].flags = 0;\n+\t\t\t\tshaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;\n+\t\t\t\tshaderStages[1].module = program.m_fsh->m_module;\n+\t\t\t\tshaderStages[1].pName  = "main";\n+\t\t\t\tshaderStages[1].pSpecializationInfo = NULL;\n+\t\t\t}\n \n \t\t\tVkPipelineViewportStateCreateInfo viewportState;\n \t\t\tviewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;\n@@ -3702,7 +3700,7 @@ VK_IMPORT_DEVICE\n \t\t\tgraphicsPipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;\n \t\t\tgraphicsPipeline.pNext = NULL;\n \t\t\tgraphicsPipeline.flags = 0;\n-\t\t\tgraphicsPipeline.stageCount = BX_COUNTOF(shaderStages);\n+\t\t\tgraphicsPipeline.stageCount = NULL == program.m_fsh ? 1 : 2;\n \t\t\tgraphicsPipeline.pStages    = shaderStages;\n \t\t\tgraphicsPipeline.pVertexInputState   = &vertexInputState;\n \t\t\tgraphicsPipeline.pInputAssemblyState = &inputAssemblyState;\n@@ -3935,7 +3933,7 @@ VK_IMPORT_DEVICE\n \t\t\tif (0 < total)\n \t\t\t{\n \t\t\t\tuint32_t vsUniformBinding = program.m_vsh->m_uniformBinding;\n-\t\t\t\tuint32_t fsUniformBinding = program.m_fsh ? program.m_fsh->m_uniformBinding : 0;\n+\t\t\t\tuint32_t fsUniformBinding = NULL != program.m_fsh ? program.m_fsh->m_uniformBinding : 0;\n \n \t\t\t\tif (vsize > 0)\n \t\t\t\t{\n@@ -4080,7 +4078,7 @@ VK_IMPORT_DEVICE\n \t\t\tif (isValid(fbh) )\n \t\t\t{\n \t\t\t\tconst FrameBufferVK& fb = m_frameBuffers[fbh.idx];\n-\t\t\t\tnumMrt = bx::max((uint8_t)1, fb.m_num);\n+\t\t\t\tnumMrt = fb.m_num;\n \t\t\t}\n \n \t\t\tVkClearAttachment attachments[BGFX_CONFIG_MAX_FRAME_BUFFERS];\n@@ -4305,8 +4303,6 @@ VK_IMPORT_DEVICE\n \t\tVkFence  m_fence;\n \t\tVkRenderPass m_renderPass;\n \t\tVkDescriptorPool m_descriptorPool;\n-//\t\tVkDescriptorSetLayout m_descriptorSetLayout;\n-//\t\tVkPipelineLayout m_pipelineLayout;\n \t\tVkPipelineCache m_pipelineCache;\n \t\tVkCommandPool m_commandPool;\n \n@@ -4338,8 +4334,6 @@ VK_IMPORT_DEVICE\n \n \t\tuint8_t m_fsScratch[64<<10];\n \t\tuint8_t m_vsScratch[64<<10];\n-\t\tuint32_t m_fsChanges;\n-\t\tuint32_t m_vsChanges;\n \n \t\tuint32_t m_backBufferColorIdx;\n \t\tFrameBufferHandle m_fbh;\n@@ -6113,9 +6107,6 @@ VK_DESTROY\n \n \t\t\t\t\t\t\toffset = scratchBuffer.m_pos;\n \n-\t\t\t\t\t\t\tm_vsChanges = 0;\n-\t\t\t\t\t\t\tm_fsChanges = 0;\n-\n \t\t\t\t\t\t\tbx::memCopy(&scratchBuffer.m_data[scratchBuffer.m_pos], m_vsScratch, program.m_vsh->m_size);\n \n \t\t\t\t\t\t\tscratchBuffer.m_pos += vsize;\n@@ -6425,8 +6416,6 @@ VK_DESTROY\n \t\t\t\t\t\t\t\tbx::memCopy(&scratchBuffer.m_data[scratchBuffer.m_pos + vsize], m_fsScratch, program.m_fsh->m_size);\n \t\t\t\t\t\t\t}\n \n-\t\t\t\t\t\t\tm_vsChanges = 0;\n-\t\t\t\t\t\t\tm_fsChanges = 0;\n \t\t\t\t\t\t\tscratchBuffer.m_pos += total;\n \t\t\t\t\t\t}\n \n', 'commit_subject': 'Vulkan cleanup and minor bug fixes (#2129)', 'repo_name': 'bkaradzic/bgfx', 'committer': {'name': 'GitHub', 'email': '1505422b2465e9a84f6fdfaa161078890c593f06@github.com', 'time_sec': '1588537651', 'tz_offset': '-420', 'date': {'seconds': '1588537651'}}, 'commit_tree': '3310eb7e7fdf911a917f7c0d39216835fdf38b49', 'commit_message': 'Vulkan cleanup and minor bug fixes (#2129)\n\n', 'language_name': 'Shell', 'license': 'bsd-2-clause', 'hunks': '@@ -2804,9 +2804,6 @@ VK_IMPORT_DEVICE\n \t\t\twds[2].pBufferInfo = NULL;\n \t\t\twds[2].pTexelBufferView = NULL;\n \n-\t\t\tm_vsChanges = 0;\n-\t\t\tm_fsChanges = 0;\n-\n \t\t\tvkUpdateDescriptorSets(m_device, 3, wds, 0, NULL);\n \t\t\tvkCmdBindDescriptorSets(\n \t\t\t\tm_commandBuffer\n@@ -3013,16 +3010,13 @@ VK_IMPORT_DEVICE\n \n \t\tvoid setShaderUniform(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs)\n \t\t{\n-\t\t\tBX_UNUSED(_flags, _regIndex, _val, _numRegs);\n-\t\t\tif (_flags&BGFX_UNIFORM_FRAGMENTBIT)\n+\t\t\tif (_flags & BGFX_UNIFORM_FRAGMENTBIT)\n \t\t\t{\n \t\t\t\tbx::memCopy(&m_fsScratch[_regIndex], _val, _numRegs*16);\n-\t\t\t\tm_fsChanges += _numRegs;\n \t\t\t}\n \t\t\telse\n \t\t\t{\n \t\t\t\tbx::memCopy(&m_vsScratch[_regIndex], _val, _numRegs*16);\n-\t\t\t\tm_vsChanges += _numRegs;\n \t\t\t}\n \t\t}\n \n@@ -3070,9 +3064,6 @@ VK_IMPORT_DEVICE\n //\t\t\t\t\t, NULL\n //\t\t\t\t\t);\n //\t\t\t}\n-//\n-//\t\t\tm_vsChanges = 0;\n-//\t\t\tm_fsChanges = 0;\n //\t\t}\n \n \t\tvoid setFrameBuffer(FrameBufferHandle _fbh, bool _msaa = true)\n@@ -3604,7 +3595,10 @@ VK_IMPORT_DEVICE\n \t\t\tmurmur.add(_stencil);\n \t\t\tmurmur.add(program.m_vsh->m_hash);\n \t\t\tmurmur.add(program.m_vsh->m_attrMask, sizeof(program.m_vsh->m_attrMask) );\n-\t\t\tmurmur.add(program.m_fsh->m_hash);\n+\t\t\tif (NULL != program.m_fsh)\n+\t\t\t{\n+\t\t\t\tmurmur.add(program.m_fsh->m_hash);\n+\t\t\t}\n \t\t\tfor (uint8_t ii = 0; ii < _numStreams; ++ii)\n \t\t\t{\n \t\t\t\tmurmur.add(_layouts[ii]->m_hash);\n@@ -3670,13 +3664,17 @@ VK_IMPORT_DEVICE\n \t\t\tshaderStages[0].module = program.m_vsh->m_module;\n \t\t\tshaderStages[0].pName  = "main";\n \t\t\tshaderStages[0].pSpecializationInfo = NULL;\n-\t\t\tshaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n-\t\t\tshaderStages[1].pNext = NULL;\n-\t\t\tshaderStages[1].flags = 0;\n-\t\t\tshaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;\n-\t\t\tshaderStages[1].module = program.m_fsh->m_module;\n-\t\t\tshaderStages[1].pName  = "main";\n-\t\t\tshaderStages[1].pSpecializationInfo = NULL;\n+\n+\t\t\tif (NULL != program.m_fsh)\n+\t\t\t{\n+\t\t\t\tshaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n+\t\t\t\tshaderStages[1].pNext = NULL;\n+\t\t\t\tshaderStages[1].flags = 0;\n+\t\t\t\tshaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;\n+\t\t\t\tshaderStages[1].module = program.m_fsh->m_module;\n+\t\t\t\tshaderStages[1].pName  = "main";\n+\t\t\t\tshaderStages[1].pSpecializationInfo = NULL;\n+\t\t\t}\n \n \t\t\tVkPipelineViewportStateCreateInfo viewportState;\n \t\t\tviewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;\n@@ -3702,7 +3700,7 @@ VK_IMPORT_DEVICE\n \t\t\tgraphicsPipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;\n \t\t\tgraphicsPipeline.pNext = NULL;\n \t\t\tgraphicsPipeline.flags = 0;\n-\t\t\tgraphicsPipeline.stageCount = BX_COUNTOF(shaderStages);\n+\t\t\tgraphicsPipeline.stageCount = NULL == program.m_fsh ? 1 : 2;\n \t\t\tgraphicsPipeline.pStages    = shaderStages;\n \t\t\tgraphicsPipeline.pVertexInputState   = &vertexInputState;\n \t\t\tgraphicsPipeline.pInputAssemblyState = &inputAssemblyState;\n@@ -3935,7 +3933,7 @@ VK_IMPORT_DEVICE\n \t\t\tif (0 < total)\n \t\t\t{\n \t\t\t\tuint32_t vsUniformBinding = program.m_vsh->m_uniformBinding;\n-\t\t\t\tuint32_t fsUniformBinding = program.m_fsh ? program.m_fsh->m_uniformBinding : 0;\n+\t\t\t\tuint32_t fsUniformBinding = NULL != program.m_fsh ? program.m_fsh->m_uniformBinding : 0;\n \n \t\t\t\tif (vsize > 0)\n \t\t\t\t{\n@@ -4080,7 +4078,7 @@ VK_IMPORT_DEVICE\n \t\t\tif (isValid(fbh) )\n \t\t\t{\n \t\t\t\tconst FrameBufferVK& fb = m_frameBuffers[fbh.idx];\n-\t\t\t\tnumMrt = bx::max((uint8_t)1, fb.m_num);\n+\t\t\t\tnumMrt = fb.m_num;\n \t\t\t}\n \n \t\t\tVkClearAttachment attachments[BGFX_CONFIG_MAX_FRAME_BUFFERS];\n@@ -4305,8 +4303,6 @@ VK_IMPORT_DEVICE\n \t\tVkFence  m_fence;\n \t\tVkRenderPass m_renderPass;\n \t\tVkDescriptorPool m_descriptorPool;\n-//\t\tVkDescriptorSetLayout m_descriptorSetLayout;\n-//\t\tVkPipelineLayout m_pipelineLayout;\n \t\tVkPipelineCache m_pipelineCache;\n \t\tVkCommandPool m_commandPool;\n \n@@ -4338,8 +4334,6 @@ VK_IMPORT_DEVICE\n \n \t\tuint8_t m_fsScratch[64<<10];\n \t\tuint8_t m_vsScratch[64<<10];\n-\t\tuint32_t m_fsChanges;\n-\t\tuint32_t m_vsChanges;\n \n \t\tuint32_t m_backBufferColorIdx;\n \t\tFrameBufferHandle m_fbh;\n@@ -6113,9 +6107,6 @@ VK_DESTROY\n \n \t\t\t\t\t\t\toffset = scratchBuffer.m_pos;\n \n-\t\t\t\t\t\t\tm_vsChanges = 0;\n-\t\t\t\t\t\t\tm_fsChanges = 0;\n-\n \t\t\t\t\t\t\tbx::memCopy(&scratchBuffer.m_data[scratchBuffer.m_pos], m_vsScratch, program.m_vsh->m_size);\n \n \t\t\t\t\t\t\tscratchBuffer.m_pos += vsize;\n@@ -6425,8 +6416,6 @@ VK_DESTROY\n \t\t\t\t\t\t\t\tbx::memCopy(&scratchBuffer.m_data[scratchBuffer.m_pos + vsize], m_fsScratch, program.m_fsh->m_size);\n \t\t\t\t\t\t\t}\n \n-\t\t\t\t\t\t\tm_vsChanges = 0;\n-\t\t\t\t\t\t\tm_fsChanges = 0;\n \t\t\t\t\t\t\tscratchBuffer.m_pos += total;\n \t\t\t\t\t\t}\n \n', 'addition_count': 19, 'deletion_count': 30, 'src_file': 'a/src/renderer_vk.cpp', 'tgt_file': 'b/src/renderer_vk.cpp', 'file_extension': '.cpp', 'before_file': '/*\n * Copyright 2011-2022 Branimir Karadzic. All rights reserved.\n * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE\n */\n\n#include "bgfx_p.h"\n\n#if BGFX_CONFIG_RENDERER_VULKAN\n#\tinclude <bx/pixelformat.h>\n#\tinclude "renderer_vk.h"\n#\tinclude "shader_spirv.h"\n\n#if BX_PLATFORM_OSX\n#\timport <Cocoa/Cocoa.h>\n#\timport <Foundation/Foundation.h>\n#\timport <QuartzCore/QuartzCore.h>\n#\timport <Metal/Metal.h>\n#endif // BX_PLATFORM_OSX\n\nnamespace bgfx { namespace vk\n{\n\tstatic char s_viewName[BGFX_CONFIG_MAX_VIEWS][BGFX_CONFIG_MAX_VIEW_NAME];\n\n\tinline void setViewType(ViewId _view, const bx::StringView _str)\n\t{\n\t\tif (BX_ENABLED(BGFX_CONFIG_DEBUG_ANNOTATION || BGFX_CONFIG_PROFILER) )\n\t\t{\n\t\t\tbx::memCopy(&s_viewName[_view][3], _str.getPtr(), _str.getLength() );\n\t\t}\n\t}\n\n\tstruct PrimInfo\n\t{\n\t\tVkPrimitiveTopology m_topology;\n\t\tuint32_t m_min;\n\t\tuint32_t m_div;\n\t\tuint32_t m_sub;\n\t};\n\n\tstatic const PrimInfo s_primInfo[] =\n\t{\n\t\t{ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,  3, 3, 0 },\n\t\t{ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 3, 1, 2 },\n\t\t{ VK_PRIMITIVE_TOPOLOGY_LINE_LIST,      2, 2, 0 },\n\t\t{ VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,     2, 1, 1 },\n\t\t{ VK_PRIMITIVE_TOPOLOGY_POINT_LIST,     1, 1, 0 },\n\t\t{ VK_PRIMITIVE_TOPOLOGY_MAX_ENUM,       0, 0, 0 },\n\t};\n\tBX_STATIC_ASSERT(Topology::Count == BX_COUNTOF(s_primInfo)-1);\n\n\tstatic MsaaSamplerVK s_msaa[] =\n\t{\n\t\t{  1, VK_SAMPLE_COUNT_1_BIT },\n\t\t{  2, VK_SAMPLE_COUNT_2_BIT },\n\t\t{  4, VK_SAMPLE_COUNT_4_BIT },\n\t\t{  8, VK_SAMPLE_COUNT_8_BIT },\n\t\t{ 16, VK_SAMPLE_COUNT_16_BIT },\n\t};\n\n\tstatic const VkBlendFactor s_blendFactor[][2] =\n\t{\n\t\t{ VkBlendFactor(0),                         VkBlendFactor(0)                         }, // ignored\n\t\t{ VK_BLEND_FACTOR_ZERO,                     VK_BLEND_FACTOR_ZERO                     }, // ZERO\n\t\t{ VK_BLEND_FACTOR_ONE,                      VK_BLEND_FACTOR_ONE                      }, // ONE\n\t\t{ VK_BLEND_FACTOR_SRC_COLOR,                VK_BLEND_FACTOR_SRC_ALPHA                }, // SRC_COLOR\n\t\t{ VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,      VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA      }, // INV_SRC_COLOR\n\t\t{ VK_BLEND_FACTOR_SRC_ALPHA,                VK_BLEND_FACTOR_SRC_ALPHA                }, // SRC_ALPHA\n\t\t{ VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,      VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA      }, // INV_SRC_ALPHA\n\t\t{ VK_BLEND_FACTOR_DST_ALPHA,                VK_BLEND_FACTOR_DST_ALPHA                }, // DST_ALPHA\n\t\t{ VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,      VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA      }, // INV_DST_ALPHA\n\t\t{ VK_BLEND_FACTOR_DST_COLOR,                VK_BLEND_FACTOR_DST_ALPHA                }, // DST_COLOR\n\t\t{ VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,      VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA      }, // INV_DST_COLOR\n\t\t{ VK_BLEND_FACTOR_SRC_ALPHA,                VK_BLEND_FACTOR_ONE                      }, // SRC_ALPHA_SAT\n\t\t{ VK_BLEND_FACTOR_CONSTANT_COLOR,           VK_BLEND_FACTOR_CONSTANT_COLOR           }, // FACTOR\n\t\t{ VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR }, // INV_FACTOR\n\t};\n\n\tstatic const VkBlendOp s_blendEquation[] =\n\t{\n\t\tVK_BLEND_OP_ADD,\n\t\tVK_BLEND_OP_SUBTRACT,\n\t\tVK_BLEND_OP_REVERSE_SUBTRACT,\n\t\tVK_BLEND_OP_MIN,\n\t\tVK_BLEND_OP_MAX,\n\t};\n\n\tstatic const VkCompareOp s_cmpFunc[] =\n\t{\n\t\tVkCompareOp(0), // ignored\n\t\tVK_COMPARE_OP_LESS,\n\t\tVK_COMPARE_OP_LESS_OR_EQUAL,\n\t\tVK_COMPARE_OP_EQUAL,\n\t\tVK_COMPARE_OP_GREATER_OR_EQUAL,\n\t\tVK_COMPARE_OP_GREATER,\n\t\tVK_COMPARE_OP_NOT_EQUAL,\n\t\tVK_COMPARE_OP_NEVER,\n\t\tVK_COMPARE_OP_ALWAYS,\n\t};\n\n\tstatic const VkStencilOp s_stencilOp[] =\n\t{\n\t\tVK_STENCIL_OP_ZERO,\n\t\tVK_STENCIL_OP_KEEP,\n\t\tVK_STENCIL_OP_REPLACE,\n\t\tVK_STENCIL_OP_INCREMENT_AND_WRAP,\n\t\tVK_STENCIL_OP_INCREMENT_AND_CLAMP,\n\t\tVK_STENCIL_OP_DECREMENT_AND_WRAP,\n\t\tVK_STENCIL_OP_DECREMENT_AND_CLAMP,\n\t\tVK_STENCIL_OP_INVERT,\n\t};\n\n\tstatic const VkCullModeFlagBits s_cullMode[] =\n\t{\n\t\tVK_CULL_MODE_NONE,\n\t\tVK_CULL_MODE_FRONT_BIT,\n\t\tVK_CULL_MODE_BACK_BIT,\n\t};\n\n\tstatic const VkSamplerAddressMode s_textureAddress[] =\n\t{\n\t\tVK_SAMPLER_ADDRESS_MODE_REPEAT,\n\t\tVK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,\n\t\tVK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,\n\t\tVK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,\n\t};\n\n\tstruct PresentMode\n\t{\n\t\tVkPresentModeKHR mode;\n\t\tbool             vsync;\n\t\tconst char*      name;\n\t};\n\n\tstatic const PresentMode s_presentMode[] =\n\t{\n\t\t{ VK_PRESENT_MODE_FIFO_KHR,         true,  "VK_PRESENT_MODE_FIFO_KHR"         },\n\t\t{ VK_PRESENT_MODE_FIFO_RELAXED_KHR, true,  "VK_PRESENT_MODE_FIFO_RELAXED_KHR" },\n\t\t{ VK_PRESENT_MODE_MAILBOX_KHR,      true,  "VK_PRESENT_MODE_MAILBOX_KHR"      },\n\t\t{ VK_PRESENT_MODE_IMMEDIATE_KHR,    false, "VK_PRESENT_MODE_IMMEDIATE_KHR"    },\n\t};\n\n#define VK_IMPORT_FUNC(_optional, _func) PFN_##_func _func\n#define VK_IMPORT_INSTANCE_FUNC VK_IMPORT_FUNC\n#define VK_IMPORT_DEVICE_FUNC   VK_IMPORT_FUNC\nVK_IMPORT\nVK_IMPORT_INSTANCE\nVK_IMPORT_DEVICE\n#undef VK_IMPORT_DEVICE_FUNC\n#undef VK_IMPORT_INSTANCE_FUNC\n#undef VK_IMPORT_FUNC\n\n\tstruct TextureFormatInfo\n\t{\n\t\tVkFormat m_fmt;\n\t\tVkFormat m_fmtSrv;\n\t\tVkFormat m_fmtDsv;\n\t\tVkFormat m_fmtSrgb;\n\t\tVkComponentMapping m_mapping;\n\t};\n\n\tstatic const TextureFormatInfo s_textureFormat[] =\n\t{\n#define $_ VK_COMPONENT_SWIZZLE_IDENTITY\n#define $0 VK_COMPONENT_SWIZZLE_ZERO\n#define $1 VK_COMPONENT_SWIZZLE_ONE\n#define $R VK_COMPONENT_SWIZZLE_R\n#define $G VK_COMPONENT_SWIZZLE_G\n#define $B VK_COMPONENT_SWIZZLE_B\n#define $A VK_COMPONENT_SWIZZLE_A\n\t\t{ VK_FORMAT_BC1_RGB_UNORM_BLOCK,       VK_FORMAT_BC1_RGB_UNORM_BLOCK,      VK_FORMAT_UNDEFINED,           VK_FORMAT_BC1_RGB_SRGB_BLOCK,       { $_, $_, $_, $_ } }, // BC1\n\t\t{ VK_FORMAT_BC2_UNORM_BLOCK,           VK_FORMAT_BC2_UNORM_BLOCK,          VK_FORMAT_UNDEFINED,           VK_FORMAT_BC2_SRGB_BLOCK,           { $_, $_, $_, $_ } }, // BC2\n\t\t{ VK_FORMAT_BC3_UNORM_BLOCK,           VK_FORMAT_BC3_UNORM_BLOCK,          VK_FORMAT_UNDEFINED,           VK_FORMAT_BC3_SRGB_BLOCK,           { $_, $_, $_, $_ } }, // BC3\n\t\t{ VK_FORMAT_BC4_UNORM_BLOCK,           VK_FORMAT_BC4_UNORM_BLOCK,          VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // BC4\n\t\t{ VK_FORMAT_BC5_UNORM_BLOCK,           VK_FORMAT_BC5_UNORM_BLOCK,          VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // BC5\n\t\t{ VK_FORMAT_BC6H_SFLOAT_BLOCK,         VK_FORMAT_BC6H_SFLOAT_BLOCK,        VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // BC6H\n\t\t{ VK_FORMAT_BC7_UNORM_BLOCK,           VK_FORMAT_BC7_UNORM_BLOCK,          VK_FORMAT_UNDEFINED,           VK_FORMAT_BC7_SRGB_BLOCK,           { $_, $_, $_, $_ } }, // BC7\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // ETC1\n\t\t{ VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK,   VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK,   { $_, $_, $_, $_ } }, // ETC2\n\t\t{ VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ETC2A\n\t\t{ VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, { $_, $_, $_, $_ } }, // ETC2A1\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // PTC12\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // PTC14\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // PTC12A\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // PTC14A\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // PTC22\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // PTC24\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // ATC\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // ATCE\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // ATCI\n\t\t{ VK_FORMAT_ASTC_4x4_UNORM_BLOCK,      VK_FORMAT_ASTC_4x4_UNORM_BLOCK,     VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_4x4_SRGB_BLOCK,      { $_, $_, $_, $_ } }, // ASTC4x4\n\t\t{ VK_FORMAT_ASTC_5x4_UNORM_BLOCK,      VK_FORMAT_ASTC_5x4_UNORM_BLOCK,     VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_5x4_SRGB_BLOCK,      { $_, $_, $_, $_ } }, // ASTC5x4\n\t\t{ VK_FORMAT_ASTC_5x5_UNORM_BLOCK,      VK_FORMAT_ASTC_5x5_UNORM_BLOCK,     VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_5x5_SRGB_BLOCK,      { $_, $_, $_, $_ } }, // ASTC5x5\n\t\t{ VK_FORMAT_ASTC_6x5_UNORM_BLOCK,      VK_FORMAT_ASTC_6x5_UNORM_BLOCK,     VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_6x5_SRGB_BLOCK,      { $_, $_, $_, $_ } }, // ASTC6x5\n\t\t{ VK_FORMAT_ASTC_6x6_UNORM_BLOCK,      VK_FORMAT_ASTC_6x6_UNORM_BLOCK,     VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_6x6_SRGB_BLOCK,      { $_, $_, $_, $_ } }, // ASTC6x6\n\t\t{ VK_FORMAT_ASTC_8x5_UNORM_BLOCK,      VK_FORMAT_ASTC_8x5_UNORM_BLOCK,     VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_8x5_SRGB_BLOCK,      { $_, $_, $_, $_ } }, // ASTC8x5\n\t\t{ VK_FORMAT_ASTC_8x6_UNORM_BLOCK,      VK_FORMAT_ASTC_8x6_UNORM_BLOCK,     VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_8x6_SRGB_BLOCK,      { $_, $_, $_, $_ } }, // ASTC8x6\n\t\t{ VK_FORMAT_ASTC_8x8_UNORM_BLOCK,      VK_FORMAT_ASTC_8x8_UNORM_BLOCK,     VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_8x8_SRGB_BLOCK,      { $_, $_, $_, $_ } }, // ASTC8x8\n\t\t{ VK_FORMAT_ASTC_10x5_UNORM_BLOCK,     VK_FORMAT_ASTC_10x5_UNORM_BLOCK,    VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_10x5_SRGB_BLOCK,     { $_, $_, $_, $_ } }, // ASTC10x5\n\t\t{ VK_FORMAT_ASTC_10x6_UNORM_BLOCK,     VK_FORMAT_ASTC_10x6_UNORM_BLOCK,    VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_10x6_SRGB_BLOCK,     { $_, $_, $_, $_ } }, // ASTC10x6\n\t\t{ VK_FORMAT_ASTC_10x8_UNORM_BLOCK,     VK_FORMAT_ASTC_10x8_UNORM_BLOCK,    VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_10x8_SRGB_BLOCK,     { $_, $_, $_, $_ } }, // ASTC10x8\n\t\t{ VK_FORMAT_ASTC_10x10_UNORM_BLOCK,    VK_FORMAT_ASTC_10x10_UNORM_BLOCK,   VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_10x10_SRGB_BLOCK,    { $_, $_, $_, $_ } }, // ASTC10x10\n\t\t{ VK_FORMAT_ASTC_12x10_UNORM_BLOCK,    VK_FORMAT_ASTC_12x10_UNORM_BLOCK,   VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_12x10_SRGB_BLOCK,    { $_, $_, $_, $_ } }, // ASTC12x10\n\t\t{ VK_FORMAT_ASTC_12x12_UNORM_BLOCK,    VK_FORMAT_ASTC_12x12_UNORM_BLOCK,   VK_FORMAT_UNDEFINED,           VK_FORMAT_ASTC_12x12_SRGB_BLOCK,    { $_, $_, $_, $_ } }, // ASTC12x12\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // Unknown\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R1\n\t\t{ VK_FORMAT_R8_UNORM,                  VK_FORMAT_R8_UNORM,                 VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $0, $0, $0, $R } }, // A8\n\t\t{ VK_FORMAT_R8_UNORM,                  VK_FORMAT_R8_UNORM,                 VK_FORMAT_UNDEFINED,           VK_FORMAT_R8_SRGB,                  { $_, $_, $_, $_ } }, // R8\n\t\t{ VK_FORMAT_R8_SINT,                   VK_FORMAT_R8_SINT,                  VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R8I\n\t\t{ VK_FORMAT_R8_UINT,                   VK_FORMAT_R8_UINT,                  VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R8U\n\t\t{ VK_FORMAT_R8_SNORM,                  VK_FORMAT_R8_SNORM,                 VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R8S\n\t\t{ VK_FORMAT_R16_UNORM,                 VK_FORMAT_R16_UNORM,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R16\n\t\t{ VK_FORMAT_R16_SINT,                  VK_FORMAT_R16_SINT,                 VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R16I\n\t\t{ VK_FORMAT_R16_UNORM,                 VK_FORMAT_R16_UNORM,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R16U\n\t\t{ VK_FORMAT_R16_SFLOAT,                VK_FORMAT_R16_SFLOAT,               VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R16F\n\t\t{ VK_FORMAT_R16_SNORM,                 VK_FORMAT_R16_SNORM,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R16S\n\t\t{ VK_FORMAT_R32_SINT,                  VK_FORMAT_R32_SINT,                 VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R32I\n\t\t{ VK_FORMAT_R32_UINT,                  VK_FORMAT_R32_UINT,                 VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R32U\n\t\t{ VK_FORMAT_R32_SFLOAT,                VK_FORMAT_R32_SFLOAT,               VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R32F\n\t\t{ VK_FORMAT_R8G8_UNORM,                VK_FORMAT_R8G8_UNORM,               VK_FORMAT_UNDEFINED,           VK_FORMAT_R8G8_SRGB,                { $_, $_, $_, $_ } }, // RG8\n\t\t{ VK_FORMAT_R8G8_SINT,                 VK_FORMAT_R8G8_SINT,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG8I\n\t\t{ VK_FORMAT_R8G8_UINT,                 VK_FORMAT_R8G8_UINT,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG8U\n\t\t{ VK_FORMAT_R8G8_SNORM,                VK_FORMAT_R8G8_SNORM,               VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG8S\n\t\t{ VK_FORMAT_R16G16_UNORM,              VK_FORMAT_R16G16_UNORM,             VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG16\n\t\t{ VK_FORMAT_R16G16_SINT,               VK_FORMAT_R16G16_SINT,              VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG16I\n\t\t{ VK_FORMAT_R16G16_UINT,               VK_FORMAT_R16G16_UINT,              VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG16U\n\t\t{ VK_FORMAT_R16G16_SFLOAT,             VK_FORMAT_R16G16_SFLOAT,            VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG16F\n\t\t{ VK_FORMAT_R16G16_SNORM,              VK_FORMAT_R16G16_SNORM,             VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG16S\n\t\t{ VK_FORMAT_R32G32_SINT,               VK_FORMAT_R32G32_SINT,              VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG32I\n\t\t{ VK_FORMAT_R32G32_UINT,               VK_FORMAT_R32G32_UINT,              VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG32U\n\t\t{ VK_FORMAT_R32G32_SFLOAT,             VK_FORMAT_R32G32_SFLOAT,            VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG32F\n\t\t{ VK_FORMAT_R8G8B8_UNORM,              VK_FORMAT_R8G8B8_UNORM,             VK_FORMAT_UNDEFINED,           VK_FORMAT_R8G8B8_SRGB,              { $_, $_, $_, $_ } }, // RGB8\n\t\t{ VK_FORMAT_R8G8B8_SINT,               VK_FORMAT_R8G8B8_SINT,              VK_FORMAT_UNDEFINED,           VK_FORMAT_R8G8B8_SRGB,              { $_, $_, $_, $_ } }, // RGB8I\n\t\t{ VK_FORMAT_R8G8B8_UINT,               VK_FORMAT_R8G8B8_UINT,              VK_FORMAT_UNDEFINED,           VK_FORMAT_R8G8B8_SRGB,              { $_, $_, $_, $_ } }, // RGB8U\n\t\t{ VK_FORMAT_R8G8B8_SNORM,              VK_FORMAT_R8G8B8_SNORM,             VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RGB8S\n\t\t{ VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,    VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,   VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RGB9E5F\n\t\t{ VK_FORMAT_B8G8R8A8_UNORM,            VK_FORMAT_B8G8R8A8_UNORM,           VK_FORMAT_UNDEFINED,           VK_FORMAT_B8G8R8A8_SRGB,            { $_, $_, $_, $_ } }, // BGRA8\n\t\t{ VK_FORMAT_R8G8B8A8_UNORM,            VK_FORMAT_R8G8B8A8_UNORM,           VK_FORMAT_UNDEFINED,           VK_FORMAT_R8G8B8A8_SRGB,            { $_, $_, $_, $_ } }, // RGBA8\n\t\t{ VK_FORMAT_R8G8B8A8_SINT,             VK_FORMAT_R8G8B8A8_SINT,            VK_FORMAT_UNDEFINED,           VK_FORMAT_R8G8B8A8_SRGB,            { $_, $_, $_, $_ } }, // RGBA8I\n\t\t{ VK_FORMAT_R8G8B8A8_UINT,             VK_FORMAT_R8G8B8A8_UINT,            VK_FORMAT_UNDEFINED,           VK_FORMAT_R8G8B8A8_SRGB,            { $_, $_, $_, $_ } }, // RGBA8U\n\t\t{ VK_FORMAT_R8G8B8A8_SNORM,            VK_FORMAT_R8G8B8A8_SNORM,           VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RGBA8S\n\t\t{ VK_FORMAT_R16G16B16A16_UNORM,        VK_FORMAT_R16G16B16A16_UNORM,       VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RGBA16\n\t\t{ VK_FORMAT_R16G16B16A16_SINT,         VK_FORMAT_R16G16B16A16_SINT,        VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RGBA16I\n\t\t{ VK_FORMAT_R16G16B16A16_UINT,         VK_FORMAT_R16G16B16A16_UINT,        VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RGBA16U\n\t\t{ VK_FORMAT_R16G16B16A16_SFLOAT,       VK_FORMAT_R16G16B16A16_SFLOAT,      VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RGBA16F\n\t\t{ VK_FORMAT_R16G16B16A16_SNORM,        VK_FORMAT_R16G16B16A16_SNORM,       VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RGBA16S\n\t\t{ VK_FORMAT_R32G32B32A32_SINT,         VK_FORMAT_R32G32B32A32_SINT,        VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RGBA32I\n\t\t{ VK_FORMAT_R32G32B32A32_UINT,         VK_FORMAT_R32G32B32A32_UINT,        VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RGBA32U\n\t\t{ VK_FORMAT_R32G32B32A32_SFLOAT,       VK_FORMAT_R32G32B32A32_SFLOAT,      VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RGBA32F\n\t\t{ VK_FORMAT_R5G6B5_UNORM_PACK16,       VK_FORMAT_R5G6B5_UNORM_PACK16,      VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // B5G6R5\n\t\t{ VK_FORMAT_B5G6R5_UNORM_PACK16,       VK_FORMAT_B5G6R5_UNORM_PACK16,      VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // R5G6B5\n\t\t{ VK_FORMAT_B4G4R4A4_UNORM_PACK16,     VK_FORMAT_B4G4R4A4_UNORM_PACK16,    VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $G, $R, $A, $B } }, // BGRA4\n\t\t{ VK_FORMAT_R4G4B4A4_UNORM_PACK16,     VK_FORMAT_R4G4B4A4_UNORM_PACK16,    VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $A, $B, $G, $R } }, // RGBA4\n\t\t{ VK_FORMAT_A1R5G5B5_UNORM_PACK16,     VK_FORMAT_A1R5G5B5_UNORM_PACK16,    VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // BGR5A1\n\t\t{ VK_FORMAT_A1R5G5B5_UNORM_PACK16,     VK_FORMAT_A1R5G5B5_UNORM_PACK16,    VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $B, $G, $R, $A } }, // RGB5A1\n\t\t{ VK_FORMAT_A2R10G10B10_UNORM_PACK32,  VK_FORMAT_A2R10G10B10_UNORM_PACK32, VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $B, $G, $R, $A } }, // RGB10A2\n\t\t{ VK_FORMAT_B10G11R11_UFLOAT_PACK32,   VK_FORMAT_B10G11R11_UFLOAT_PACK32,  VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // RG11B10F\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_UNDEFINED,                VK_FORMAT_UNDEFINED,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // UnknownDepth\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_R16_UNORM,                VK_FORMAT_D16_UNORM,           VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // D16\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_R32_SFLOAT,               VK_FORMAT_D32_SFLOAT_S8_UINT,  VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // D24\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_R32_SFLOAT,               VK_FORMAT_D32_SFLOAT_S8_UINT,  VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // D24S8\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_R32_SFLOAT,               VK_FORMAT_D32_SFLOAT_S8_UINT,  VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // D32\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_R32_SFLOAT,               VK_FORMAT_D32_SFLOAT,          VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // D16F\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_R32_SFLOAT,               VK_FORMAT_D32_SFLOAT,          VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // D24F\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_R32_SFLOAT,               VK_FORMAT_D32_SFLOAT,          VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // D32F\n\t\t{ VK_FORMAT_UNDEFINED,                 VK_FORMAT_R8_UINT,                  VK_FORMAT_S8_UINT,             VK_FORMAT_UNDEFINED,                { $_, $_, $_, $_ } }, // D0S8\n#undef $_\n#undef $0\n#undef $1\n#undef $R\n#undef $G\n#undef $B\n#undef $A\n\t};\n\tBX_STATIC_ASSERT(TextureFormat::Count == BX_COUNTOF(s_textureFormat) );\n\n\tstruct LayerInfo\n\t{\n\t\tbool m_supported;\n\t\tbool m_initialize;\n\t};\n\n\tstruct Layer\n\t{\n\t\tenum Enum\n\t\t{\n\t\t\tVK_LAYER_LUNARG_standard_validation,\n\t\t\tVK_LAYER_KHRONOS_validation,\n\n\t\t\tCount\n\t\t};\n\n\t\tconst char* m_name;\n\t\tuint32_t m_minVersion;\n\t\tLayerInfo m_instance;\n\t\tLayerInfo m_device;\n\t};\n\n\n\t// Layer registry\n\t//\n\tstatic Layer s_layer[] =\n\t{\n\t\t{ "VK_LAYER_LUNARG_standard_validation", 1, { false, false }, { false, false } },\n\t\t{ "VK_LAYER_KHRONOS_validation",         1, { false, false }, { false, false } },\n\t};\n\tBX_STATIC_ASSERT(Layer::Count == BX_COUNTOF(s_layer) );\n\n\tvoid updateLayer(const char* _name, uint32_t _version, bool _instanceLayer)\n\t{\n\t\tbx::StringView lyr(_name);\n\n\t\tfor (uint32_t ii = 0; ii < Layer::Count; ++ii)\n\t\t{\n\t\t\tLayer& layer = s_layer[ii];\n\t\t\tLayerInfo& layerInfo = _instanceLayer ? layer.m_instance : layer.m_device;\n\t\t\tif (!layerInfo.m_supported && layerInfo.m_initialize)\n\t\t\t{\n\t\t\t\tif (       0 == bx::strCmp(lyr, layer.m_name)\n\t\t\t\t&&  _version >= layer.m_minVersion)\n\t\t\t\t{\n\t\t\t\t\tlayerInfo.m_supported = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tstruct Extension\n\t{\n\t\tenum Enum\n\t\t{\n\t\t\tEXT_debug_utils,\n\t\t\tEXT_debug_report,\n\t\t\tEXT_memory_budget,\n\t\t\tKHR_get_physical_device_properties2,\n\t\t\tEXT_conservative_rasterization,\n\t\t\tEXT_line_rasterization,\n\t\t\tEXT_shader_viewport_index_layer,\n\t\t\tEXT_custom_border_color,\n\t\t\tKHR_draw_indirect_count,\n\n\t\t\tCount\n\t\t};\n\n\t\tconst char* m_name;\n\t\tuint32_t m_minVersion;\n\t\tbool m_instanceExt;\n\t\tbool m_supported;\n\t\tbool m_initialize;\n\t\tLayer::Enum m_layer;\n\t};\n\n\t// Extension registry\n\t//\n\tstatic Extension s_extension[] =\n\t{\n\t\t{ "VK_EXT_debug_utils",                     1, false, false, BGFX_CONFIG_DEBUG_OBJECT_NAME || BGFX_CONFIG_DEBUG_ANNOTATION, Layer::Count },\n\t\t{ "VK_EXT_debug_report",                    1, false, false, false                                                        , Layer::Count },\n\t\t{ "VK_EXT_memory_budget",                   1, false, false, true                                                         , Layer::Count },\n\t\t{ "VK_KHR_get_physical_device_properties2", 1, false, false, true                                                         , Layer::Count },\n\t\t{ "VK_EXT_conservative_rasterization",      1, false, false, true                                                         , Layer::Count },\n\t\t{ "VK_EXT_line_rasterization",              1, false, false, true                                                         , Layer::Count },\n\t\t{ "VK_EXT_shader_viewport_index_layer",     1, false, false, true                                                         , Layer::Count },\n\t\t{ "VK_EXT_custom_border_color",             1, false, false, true                                                         , Layer::Count },\n\t\t{ "VK_KHR_draw_indirect_count",             1, false, false, true                                                         , Layer::Count },\n\t};\n\tBX_STATIC_ASSERT(Extension::Count == BX_COUNTOF(s_extension) );\n\n\tbool updateExtension(const char* _name, uint32_t _version, bool _instanceExt, Extension _extensions[Extension::Count])\n\t{\n\t\tbool supported = false;\n\t\tif (BX_ENABLED(BGFX_CONFIG_RENDERER_USE_EXTENSIONS) )\n\t\t{\n\t\t\tconst bx::StringView ext(_name);\n\t\t\tfor (uint32_t ii = 0; ii < Extension::Count; ++ii)\n\t\t\t{\n\t\t\t\tExtension& extension = _extensions[ii];\n\t\t\t\tLayerInfo& layerInfo = _instanceExt\n\t\t\t\t\t? s_layer[extension.m_layer].m_instance\n\t\t\t\t\t: s_layer[extension.m_layer].m_device\n\t\t\t\t\t;\n\n\t\t\t\tif (!extension.m_supported\n\t\t\t\t&&   extension.m_initialize\n\t\t\t\t&&  (extension.m_layer == Layer::Count || layerInfo.m_supported) )\n\t\t\t\t{\n\t\t\t\t\tif (       0 == bx::strCmp(ext, extension.m_name)\n\t\t\t\t\t&&  _version >= extension.m_minVersion)\n\t\t\t\t\t{\n\t\t\t\t\t\textension.m_supported   = true;\n\t\t\t\t\t\textension.m_instanceExt = _instanceExt;\n\t\t\t\t\t\tsupported = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn supported;\n\t}\n\n\tstatic const VkFormat s_attribType[][4][2] =\n\t{\n\t\t{ // Uint8\n\t\t\t{ VK_FORMAT_R8_UINT,                 VK_FORMAT_R8_UNORM                 },\n\t\t\t{ VK_FORMAT_R8G8_UINT,               VK_FORMAT_R8G8_UNORM               },\n\t\t\t{ VK_FORMAT_R8G8B8A8_UINT,           VK_FORMAT_R8G8B8A8_UNORM           },\n\t\t\t{ VK_FORMAT_R8G8B8A8_UINT,           VK_FORMAT_R8G8B8A8_UNORM           },\n\t\t},\n\t\t{ // Uint10\n\t\t\t{ VK_FORMAT_A2R10G10B10_UINT_PACK32, VK_FORMAT_A2R10G10B10_UNORM_PACK32 },\n\t\t\t{ VK_FORMAT_A2R10G10B10_UINT_PACK32, VK_FORMAT_A2R10G10B10_UNORM_PACK32 },\n\t\t\t{ VK_FORMAT_A2R10G10B10_UINT_PACK32, VK_FORMAT_A2R10G10B10_UNORM_PACK32 },\n\t\t\t{ VK_FORMAT_A2R10G10B10_UINT_PACK32, VK_FORMAT_A2R10G10B10_UNORM_PACK32 },\n\t\t},\n\t\t{ // Int16\n\t\t\t{ VK_FORMAT_R16_SINT,                VK_FORMAT_R16_SNORM                },\n\t\t\t{ VK_FORMAT_R16G16_SINT,             VK_FORMAT_R16G16_SNORM             },\n\t\t\t{ VK_FORMAT_R16G16B16_SINT,          VK_FORMAT_R16G16B16_SNORM          },\n\t\t\t{ VK_FORMAT_R16G16B16A16_SINT,       VK_FORMAT_R16G16B16A16_SNORM       },\n\t\t},\n\t\t{ // Half\n\t\t\t{ VK_FORMAT_R16_SFLOAT,              VK_FORMAT_R16_SFLOAT               },\n\t\t\t{ VK_FORMAT_R16G16_SFLOAT,           VK_FORMAT_R16G16_SFLOAT            },\n\t\t\t{ VK_FORMAT_R16G16B16_SFLOAT,        VK_FORMAT_R16G16B16_SFLOAT         },\n\t\t\t{ VK_FORMAT_R16G16B16A16_SFLOAT,     VK_FORMAT_R16G16B16A16_SFLOAT      },\n\t\t},\n\t\t{ // Float\n\t\t\t{ VK_FORMAT_R32_SFLOAT,              VK_FORMAT_R32_SFLOAT               },\n\t\t\t{ VK_FORMAT_R32G32_SFLOAT,           VK_FORMAT_R32G32_SFLOAT            },\n\t\t\t{ VK_FORMAT_R32G32B32_SFLOAT,        VK_FORMAT_R32G32B32_SFLOAT         },\n\t\t\t{ VK_FORMAT_R32G32B32A32_SFLOAT,     VK_FORMAT_R32G32B32A32_SFLOAT      },\n\t\t},\n\t};\n\tBX_STATIC_ASSERT(AttribType::Count == BX_COUNTOF(s_attribType) );\n\n\tvoid fillVertexLayout(const ShaderVK* _vsh, VkPipelineVertexInputStateCreateInfo& _vertexInputState, const VertexLayout& _layout)\n\t{\n\t\tuint32_t numBindings = _vertexInputState.vertexBindingDescriptionCount;\n\t\tuint32_t numAttribs  = _vertexInputState.vertexAttributeDescriptionCount;\n\t\tVkVertexInputBindingDescription*   inputBinding = const_cast<VkVertexInputBindingDescription*>(_vertexInputState.pVertexBindingDescriptions + numBindings);\n\t\tVkVertexInputAttributeDescription* inputAttrib  = const_cast<VkVertexInputAttributeDescription*>(_vertexInputState.pVertexAttributeDescriptions + numAttribs);\n\n\t\tinputBinding->binding   = numBindings;\n\t\tinputBinding->stride    = _layout.m_stride;\n\t\tinputBinding->inputRate = VK_VERTEX_INPUT_RATE_VERTEX;\n\n\t\tfor (uint32_t attr = 0; attr < Attrib::Count; ++attr)\n\t\t{\n\t\t\tif (UINT16_MAX != _layout.m_attributes[attr])\n\t\t\t{\n\t\t\t\tinputAttrib->location = _vsh->m_attrRemap[attr];\n\t\t\t\tinputAttrib->binding  = numBindings;\n\n\t\t\t\tuint8_t num;\n\t\t\t\tAttribType::Enum type;\n\t\t\t\tbool normalized;\n\t\t\t\tbool asInt;\n\t\t\t\t_layout.decode(Attrib::Enum(attr), num, type, normalized, asInt);\n\t\t\t\tinputAttrib->format = s_attribType[type][num-1][normalized];\n\t\t\t\tinputAttrib->offset = _layout.m_offset[attr];\n\n\t\t\t\t++inputAttrib;\n\t\t\t\t++numAttribs;\n\t\t\t}\n\t\t}\n\n\t\t_vertexInputState.vertexBindingDescriptionCount   = numBindings + 1;\n\t\t_vertexInputState.vertexAttributeDescriptionCount = numAttribs;\n\t}\n\n\tvoid fillInstanceBinding(const ShaderVK* _vsh, VkPipelineVertexInputStateCreateInfo& _vertexInputState, uint32_t _numInstanceData)\n\t{\n\t\tBX_UNUSED(_vsh);\n\n\t\tuint32_t numBindings = _vertexInputState.vertexBindingDescriptionCount;\n\t\tuint32_t numAttribs  = _vertexInputState.vertexAttributeDescriptionCount;\n\t\tVkVertexInputBindingDescription*   inputBinding = const_cast<VkVertexInputBindingDescription*>(_vertexInputState.pVertexBindingDescriptions + numBindings);\n\t\tVkVertexInputAttributeDescription* inputAttrib  = const_cast<VkVertexInputAttributeDescription*>(_vertexInputState.pVertexAttributeDescriptions + numAttribs);\n\n\t\tinputBinding->binding   = numBindings;\n\t\tinputBinding->stride    = _numInstanceData * 16;\n\t\tinputBinding->inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;\n\n\t\tfor (uint32_t inst = 0; inst < _numInstanceData; ++inst)\n\t\t{\n\t\t\tinputAttrib->location = numAttribs;\n\t\t\tinputAttrib->binding  = numBindings;\n\t\t\tinputAttrib->format   = VK_FORMAT_R32G32B32A32_SFLOAT;\n\t\t\tinputAttrib->offset   = inst * 16;\n\n\t\t\t++numAttribs;\n\t\t\t++inputAttrib;\n\t\t}\n\n\t\t_vertexInputState.vertexBindingDescriptionCount   = numBindings + 1;\n\t\t_vertexInputState.vertexAttributeDescriptionCount = numAttribs;\n\t}\n\n\tstatic const char* s_deviceTypeName[] =\n\t{\n\t\t"Other",\n\t\t"Integrated GPU",\n\t\t"Discrete GPU",\n\t\t"Virtual GPU",\n\t\t"CPU",\n\n\t\t"Unknown?!"\n\t};\n\n\tconst char* getName(VkPhysicalDeviceType _type)\n\t{\n\t\treturn s_deviceTypeName[bx::min<int32_t>(_type, BX_COUNTOF(s_deviceTypeName)-1 )];\n\t}\n\n\tstatic const char* s_allocScopeName[] =\n\t{\n\t\t"vkCommand",\n\t\t"vkObject",\n\t\t"vkCache",\n\t\t"vkDevice",\n\t\t"vkInstance",\n\t};\n\tBX_STATIC_ASSERT(VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE == BX_COUNTOF(s_allocScopeName)-1);\n\n\tstatic void* VKAPI_PTR allocationFunction(void* _userData, size_t _size, size_t _alignment, VkSystemAllocationScope _allocationScope)\n\t{\n\t\tBX_UNUSED(_userData, _allocationScope);\n\t\treturn bx::alignedAlloc(g_allocator, _size, _alignment, s_allocScopeName[_allocationScope]);\n\t}\n\n\tstatic void* VKAPI_PTR reallocationFunction(void* _userData, void* _original, size_t _size, size_t _alignment, VkSystemAllocationScope _allocationScope)\n\t{\n\t\tBX_UNUSED(_userData, _allocationScope);\n\t\treturn bx::alignedRealloc(g_allocator, _original, _size, _alignment, s_allocScopeName[_allocationScope]);\n\t}\n\n\tstatic void VKAPI_PTR freeFunction(void* _userData, void* _memory)\n\t{\n\t\tBX_UNUSED(_userData);\n\n\t\tif (NULL == _memory)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tbx::alignedFree(g_allocator, _memory, 8);\n\t}\n\n\tstatic void VKAPI_PTR internalAllocationNotification(void* _userData, size_t _size, VkInternalAllocationType _allocationType, VkSystemAllocationScope _allocationScope)\n\t{\n\t\tBX_UNUSED(_userData, _size, _allocationType, _allocationScope);\n\t}\n\n\tstatic void VKAPI_PTR internalFreeNotification(void* _userData, size_t _size, VkInternalAllocationType _allocationType, VkSystemAllocationScope _allocationScope)\n\t{\n\t\tBX_UNUSED(_userData, _size, _allocationType, _allocationScope);\n\t}\n\n\tstatic VkAllocationCallbacks s_allocationCb =\n\t{\n\t\tNULL,\n\t\tallocationFunction,\n\t\treallocationFunction,\n\t\tfreeFunction,\n\t\tinternalAllocationNotification,\n\t\tinternalFreeNotification,\n\t};\n\n\tVkResult VKAPI_PTR stubSetDebugUtilsObjectNameEXT(VkDevice _device, const VkDebugUtilsObjectNameInfoEXT* _nameInfo)\n\t{\n\t\tBX_UNUSED(_device, _nameInfo);\n\t\treturn VK_SUCCESS;\n\t}\n\n\tvoid VKAPI_PTR stubCmdInsertDebugUtilsLabelEXT(VkCommandBuffer _commandBuffer, const VkDebugUtilsLabelEXT* _labelInfo)\n\t{\n\t\tBX_UNUSED(_commandBuffer, _labelInfo);\n\t}\n\n\tvoid VKAPI_PTR stubCmdBeginDebugUtilsLabelEXT(VkCommandBuffer _commandBuffer, const VkDebugUtilsLabelEXT* _labelInfo)\n\t{\n\t\tBX_UNUSED(_commandBuffer, _labelInfo);\n\t}\n\n\tvoid VKAPI_PTR stubCmdEndDebugUtilsLabelEXT(VkCommandBuffer _commandBuffer)\n\t{\n\t\tBX_UNUSED(_commandBuffer);\n\t}\n\n\tstatic const char* s_debugReportObjectType[] =\n\t{\n\t\t"Unknown",\n\t\t"Instance",\n\t\t"PhysicalDevice",\n\t\t"Device",\n\t\t"Queue",\n\t\t"Semaphore",\n\t\t"CommandBuffer",\n\t\t"Fence",\n\t\t"DeviceMemory",\n\t\t"Buffer",\n\t\t"Image",\n\t\t"Event",\n\t\t"QueryPool",\n\t\t"BufferView",\n\t\t"ImageView",\n\t\t"ShaderModule",\n\t\t"PipelineCache",\n\t\t"PipelineLayout",\n\t\t"RenderPass",\n\t\t"Pipeline",\n\t\t"DescriptorSetLayout",\n\t\t"Sampler",\n\t\t"DescriptorPool",\n\t\t"DescriptorSet",\n\t\t"Framebuffer",\n\t\t"CommandPool",\n\t\t"SurfaceKHR",\n\t\t"SwapchainKHR",\n\t\t"DebugReport",\n\t};\n\n\tVkBool32 VKAPI_PTR debugReportCb(\n\t\tVkDebugReportFlagsEXT _flags,\n\t\tVkDebugReportObjectTypeEXT _objectType,\n\t\tuint64_t _object,\n\t\tsize_t _location,\n\t\tint32_t _messageCode,\n\t\tconst char* _layerPrefix,\n\t\tconst char* _message,\n\t\tvoid* _userData\n\t)\n\t{\n\t\tBX_UNUSED(_flags\n\t\t\t, _objectType\n\t\t\t, _object\n\t\t\t, _location\n\t\t\t, _messageCode\n\t\t\t, _layerPrefix\n\t\t\t, _message\n\t\t\t, _userData\n\t\t\t, s_debugReportObjectType\n\t\t);\n\n\t\t// For more info about \'VUID-VkSwapchainCreateInfoKHR-imageExtent-01274\'\n\t\t// check https://github.com/KhronosGroup/Vulkan-Docs/issues/1144\n\t\tif (!bx::strFind(_message, "PointSizeMissing").isEmpty()\n\t\t||  !bx::strFind(_message, "SwapchainTooManyImages").isEmpty()\n\t\t||  !bx::strFind(_message, "SwapchainImageNotAcquired").isEmpty()\n\t\t||  !bx::strFind(_message, "VUID-VkSwapchainCreateInfoKHR-imageExtent-01274").isEmpty() )\n\t\t{\n\t\t\treturn VK_FALSE;\n\t\t}\n\t\tBX_TRACE("%c%c%c%c%c %19s, %s, %d: %s"\n\t\t\t, 0 != (_flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT        ) ? \'I\' : \'-\'\n\t\t\t, 0 != (_flags & VK_DEBUG_REPORT_WARNING_BIT_EXT            ) ? \'W\' : \'-\'\n\t\t\t, 0 != (_flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) ? \'P\' : \'-\'\n\t\t\t, 0 != (_flags & VK_DEBUG_REPORT_ERROR_BIT_EXT              ) ? \'E\' : \'-\'\n\t\t\t, 0 != (_flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT              ) ? \'D\' : \'-\'\n\t\t\t, s_debugReportObjectType[_objectType]\n\t\t\t, _layerPrefix\n\t\t\t, _messageCode\n\t\t\t, _message\n\t\t\t);\n\t\treturn VK_FALSE;\n\t}\n\n\tVkResult enumerateLayerProperties(VkPhysicalDevice _physicalDevice, uint32_t* _propertyCount, VkLayerProperties* _properties)\n\t{\n\t\treturn (VK_NULL_HANDLE == _physicalDevice)\n\t\t\t? vkEnumerateInstanceLayerProperties(_propertyCount, _properties)\n\t\t\t: vkEnumerateDeviceLayerProperties(_physicalDevice, _propertyCount, _properties)\n\t\t\t;\n\t}\n\n\tVkResult enumerateExtensionProperties(VkPhysicalDevice _physicalDevice, const char* _layerName, uint32_t* _propertyCount, VkExtensionProperties* _properties)\n\t{\n\t\treturn (VK_NULL_HANDLE == _physicalDevice)\n\t\t\t? vkEnumerateInstanceExtensionProperties(_layerName, _propertyCount, _properties)\n\t\t\t: vkEnumerateDeviceExtensionProperties(_physicalDevice, _layerName, _propertyCount, _properties)\n\t\t\t;\n\t}\n\n\tvoid dumpExtensions(VkPhysicalDevice _physicalDevice, Extension _extensions[Extension::Count])\n\t{\n\t\t{ // Global extensions.\n\t\t\tuint32_t numExtensionProperties;\n\t\t\tVkResult result = enumerateExtensionProperties(_physicalDevice\n\t\t\t\t, NULL\n\t\t\t\t, &numExtensionProperties\n\t\t\t\t, NULL\n\t\t\t\t);\n\n\t\t\tif (VK_SUCCESS == result\n\t\t\t&&  0 < numExtensionProperties)\n\t\t\t{\n\t\t\t\tVkExtensionProperties* extensionProperties = (VkExtensionProperties*)BX_ALLOC(g_allocator, numExtensionProperties * sizeof(VkExtensionProperties) );\n\t\t\t\tresult = enumerateExtensionProperties(_physicalDevice\n\t\t\t\t\t, NULL\n\t\t\t\t\t, &numExtensionProperties\n\t\t\t\t\t, extensionProperties\n\t\t\t\t\t);\n\n\t\t\t\tBX_TRACE("Global extensions (%d):"\n\t\t\t\t\t, numExtensionProperties\n\t\t\t\t\t);\n\n\t\t\t\tfor (uint32_t extension = 0; extension < numExtensionProperties; ++extension)\n\t\t\t\t{\n\t\t\t\t\tbool supported = updateExtension(\n\t\t\t\t\t\t  extensionProperties[extension].extensionName\n\t\t\t\t\t\t, extensionProperties[extension].specVersion\n\t\t\t\t\t\t, VK_NULL_HANDLE == _physicalDevice\n\t\t\t\t\t\t, _extensions\n\t\t\t\t\t\t);\n\n\t\t\t\t\tBX_TRACE("\\tv%-3d %s%s"\n\t\t\t\t\t\t, extensionProperties[extension].specVersion\n\t\t\t\t\t\t, extensionProperties[extension].extensionName\n\t\t\t\t\t\t, supported ? " (supported)" : "", extensionProperties[extension].extensionName\n\t\t\t\t\t\t);\n\n\t\t\t\t\tBX_UNUSED(supported);\n\t\t\t\t}\n\n\t\t\t\tBX_FREE(g_allocator, extensionProperties);\n\t\t\t}\n\t\t}\n\n\t\t// Layer extensions.\n\t\tuint32_t numLayerProperties;\n\t\tVkResult result = enumerateLayerProperties(_physicalDevice, &numLayerProperties, NULL);\n\n\t\tif (VK_SUCCESS == result\n\t\t&&  0 < numLayerProperties)\n\t\t{\n\t\t\tVkLayerProperties* layerProperties = (VkLayerProperties*)BX_ALLOC(g_allocator, numLayerProperties * sizeof(VkLayerProperties) );\n\t\t\tresult = enumerateLayerProperties(_physicalDevice, &numLayerProperties, layerProperties);\n\n\t\t\tchar indent = VK_NULL_HANDLE == _physicalDevice ? \'\\0\' : \'\\t\';\n\t\t\tBX_UNUSED(indent);\n\n\t\t\tBX_TRACE("%cLayer extensions (%d):"\n\t\t\t\t, indent\n\t\t\t\t, numLayerProperties\n\t\t\t\t);\n\t\t\tfor (uint32_t layer = 0; layer < numLayerProperties; ++layer)\n\t\t\t{\n\t\t\t\tupdateLayer(\n\t\t\t\t\t  layerProperties[layer].layerName\n\t\t\t\t\t, layerProperties[layer].implementationVersion\n\t\t\t\t\t, VK_NULL_HANDLE == _physicalDevice\n\t\t\t\t\t);\n\n\t\t\t\tBX_TRACE("%c\\t%s (s: 0x%08x, i: 0x%08x), %s"\n\t\t\t\t\t, indent\n\t\t\t\t\t, layerProperties[layer].layerName\n\t\t\t\t\t, layerProperties[layer].specVersion\n\t\t\t\t\t, layerProperties[layer].implementationVersion\n\t\t\t\t\t, layerProperties[layer].description\n\t\t\t\t\t);\n\t\t\t\tuint32_t numExtensionProperties;\n\t\t\t\tresult = enumerateExtensionProperties(_physicalDevice\n\t\t\t\t\t, layerProperties[layer].layerName\n\t\t\t\t\t, &numExtensionProperties\n\t\t\t\t\t, NULL\n\t\t\t\t\t);\n\n\t\t\t\tif (VK_SUCCESS == result\n\t\t\t\t&&  0 < numExtensionProperties)\n\t\t\t\t{\n\t\t\t\t\tVkExtensionProperties* extensionProperties = (VkExtensionProperties*)BX_ALLOC(g_allocator, numExtensionProperties * sizeof(VkExtensionProperties) );\n\t\t\t\t\tresult = enumerateExtensionProperties(_physicalDevice\n\t\t\t\t\t\t, layerProperties[layer].layerName\n\t\t\t\t\t\t, &numExtensionProperties\n\t\t\t\t\t\t, extensionProperties\n\t\t\t\t\t\t);\n\n\t\t\t\t\tfor (uint32_t extension = 0; extension < numExtensionProperties; ++extension)\n\t\t\t\t\t{\n\t\t\t\t\t\tbool supported = updateExtension(\n\t\t\t\t\t\t\t  extensionProperties[extension].extensionName\n\t\t\t\t\t\t\t, extensionProperties[extension].specVersion\n\t\t\t\t\t\t\t, VK_NULL_HANDLE == _physicalDevice\n\t\t\t\t\t\t\t, _extensions\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\tBX_TRACE("%c\\t\\t%s (s: 0x%08x)"\n\t\t\t\t\t\t\t, indent\n\t\t\t\t\t\t\t, extensionProperties[extension].extensionName\n\t\t\t\t\t\t\t, extensionProperties[extension].specVersion\n\t\t\t\t\t\t\t, supported ? " (supported)" : "", extensionProperties[extension].extensionName\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\tBX_UNUSED(supported);\n\t\t\t\t\t}\n\n\t\t\t\t\tBX_FREE(g_allocator, extensionProperties);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tBX_FREE(g_allocator, layerProperties);\n\t\t}\n\t}\n\n\tconst char* getName(VkResult _result)\n\t{\n\t\tswitch (_result)\n\t\t{\n#define VKENUM(_ty) case _ty: return #_ty\n\t\t\tVKENUM(VK_SUCCESS);\n\t\t\tVKENUM(VK_NOT_READY);\n\t\t\tVKENUM(VK_TIMEOUT);\n\t\t\tVKENUM(VK_EVENT_SET);\n\t\t\tVKENUM(VK_EVENT_RESET);\n\t\t\tVKENUM(VK_INCOMPLETE);\n\t\t\tVKENUM(VK_ERROR_OUT_OF_HOST_MEMORY);\n\t\t\tVKENUM(VK_ERROR_OUT_OF_DEVICE_MEMORY);\n\t\t\tVKENUM(VK_ERROR_OUT_OF_POOL_MEMORY);\n\t\t\tVKENUM(VK_ERROR_FRAGMENTED_POOL);\n\t\t\tVKENUM(VK_ERROR_INITIALIZATION_FAILED);\n\t\t\tVKENUM(VK_ERROR_DEVICE_LOST);\n\t\t\tVKENUM(VK_ERROR_MEMORY_MAP_FAILED);\n\t\t\tVKENUM(VK_ERROR_LAYER_NOT_PRESENT);\n\t\t\tVKENUM(VK_ERROR_EXTENSION_NOT_PRESENT);\n\t\t\tVKENUM(VK_ERROR_FEATURE_NOT_PRESENT);\n\t\t\tVKENUM(VK_ERROR_INCOMPATIBLE_DRIVER);\n\t\t\tVKENUM(VK_ERROR_TOO_MANY_OBJECTS);\n\t\t\tVKENUM(VK_ERROR_FORMAT_NOT_SUPPORTED);\n\t\t\tVKENUM(VK_ERROR_SURFACE_LOST_KHR);\n\t\t\tVKENUM(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);\n\t\t\tVKENUM(VK_SUBOPTIMAL_KHR);\n\t\t\tVKENUM(VK_ERROR_OUT_OF_DATE_KHR);\n\t\t\tVKENUM(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);\n\t\t\tVKENUM(VK_ERROR_VALIDATION_FAILED_EXT);\n#undef VKENUM\n\t\t\tdefault: break;\n\t\t}\n\n\t\tBX_WARN(false, "Unknown VkResult? %x", _result);\n\t\treturn "<VkResult?>";\n\t}\n\n\ttemplate<typename Ty>\n\tconstexpr VkObjectType getType();\n\n\ttemplate<> VkObjectType getType<VkBuffer             >() { return VK_OBJECT_TYPE_BUFFER;                }\n\ttemplate<> VkObjectType getType<VkCommandPool        >() { return VK_OBJECT_TYPE_COMMAND_POOL;          }\n\ttemplate<> VkObjectType getType<VkDescriptorPool     >() { return VK_OBJECT_TYPE_DESCRIPTOR_POOL;       }\n\ttemplate<> VkObjectType getType<VkDescriptorSet      >() { return VK_OBJECT_TYPE_DESCRIPTOR_SET;        }\n\ttemplate<> VkObjectType getType<VkDescriptorSetLayout>() { return VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT; }\n\ttemplate<> VkObjectType getType<VkDeviceMemory       >() { return VK_OBJECT_TYPE_DEVICE_MEMORY;         }\n\ttemplate<> VkObjectType getType<VkFence              >() { return VK_OBJECT_TYPE_FENCE;                 }\n\ttemplate<> VkObjectType getType<VkFramebuffer        >() { return VK_OBJECT_TYPE_FRAMEBUFFER;           }\n\ttemplate<> VkObjectType getType<VkImage              >() { return VK_OBJECT_TYPE_IMAGE;                 }\n\ttemplate<> VkObjectType getType<VkImageView          >() { return VK_OBJECT_TYPE_IMAGE_VIEW;            }\n\ttemplate<> VkObjectType getType<VkPipeline           >() { return VK_OBJECT_TYPE_PIPELINE;              }\n\ttemplate<> VkObjectType getType<VkPipelineCache      >() { return VK_OBJECT_TYPE_PIPELINE_CACHE;        }\n\ttemplate<> VkObjectType getType<VkPipelineLayout     >() { return VK_OBJECT_TYPE_PIPELINE_LAYOUT;       }\n\ttemplate<> VkObjectType getType<VkQueryPool          >() { return VK_OBJECT_TYPE_QUERY_POOL;            }\n\ttemplate<> VkObjectType getType<VkRenderPass         >() { return VK_OBJECT_TYPE_RENDER_PASS;           }\n\ttemplate<> VkObjectType getType<VkSampler            >() { return VK_OBJECT_TYPE_SAMPLER;               }\n\ttemplate<> VkObjectType getType<VkSemaphore          >() { return VK_OBJECT_TYPE_SEMAPHORE;             }\n\ttemplate<> VkObjectType getType<VkShaderModule       >() { return VK_OBJECT_TYPE_SHADER_MODULE;         }\n\ttemplate<> VkObjectType getType<VkSurfaceKHR         >() { return VK_OBJECT_TYPE_SURFACE_KHR;           }\n\ttemplate<> VkObjectType getType<VkSwapchainKHR       >() { return VK_OBJECT_TYPE_SWAPCHAIN_KHR;         }\n\n\n\ttemplate<typename Ty>\n\tstatic BX_NO_INLINE void setDebugObjectName(VkDevice _device, Ty _object, const char* _format, ...)\n\t{\n\t\tif (BX_ENABLED(BGFX_CONFIG_DEBUG_OBJECT_NAME)\n\t\t&&  s_extension[Extension::EXT_debug_utils].m_supported)\n\t\t{\n\t\t\tchar temp[2048];\n\t\t\tva_list argList;\n\t\t\tva_start(argList, _format);\n\t\t\tint32_t size = bx::min<int32_t>(sizeof(temp)-1, bx::vsnprintf(temp, sizeof(temp), _format, argList) );\n\t\t\tva_end(argList);\n\t\t\ttemp[size] = \'\\0\';\n\n\t\t\tVkDebugUtilsObjectNameInfoEXT ni;\n\t\t\tni.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;\n\t\t\tni.pNext = NULL;\n\t\t\tni.objectType   = getType<Ty>();\n\t\t\tni.objectHandle = uint64_t(_object.vk);\n\t\t\tni.pObjectName  = temp;\n\n\t\t\tVK_CHECK(vkSetDebugUtilsObjectNameEXT(_device, &ni) );\n\t\t}\n\t}\n\n\tvoid setMemoryBarrier(\n\t\t  VkCommandBuffer _commandBuffer\n\t\t, VkPipelineStageFlags _srcStages\n\t\t, VkPipelineStageFlags _dstStages\n\t\t)\n\t{\n\t\tVkMemoryBarrier mb;\n\t\tmb.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;\n\t\tmb.pNext = NULL;\n\t\tmb.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;\n\t\tmb.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;\n\n\t\tvkCmdPipelineBarrier(\n\t\t\t  _commandBuffer\n\t\t\t, _srcStages\n\t\t\t, _dstStages\n\t\t\t, 0\n\t\t\t, 1\n\t\t\t, &mb\n\t\t\t, 0\n\t\t\t, NULL\n\t\t\t, 0\n\t\t\t, NULL\n\t\t\t);\n\t}\n\n\tvoid setImageMemoryBarrier(\n\t\t  VkCommandBuffer _commandBuffer\n\t\t, VkImage _image\n\t\t, VkImageAspectFlags _aspectMask\n\t\t, VkImageLayout _oldLayout\n\t\t, VkImageLayout _newLayout\n\t\t, uint32_t _baseMipLevel = 0\n\t\t, uint32_t _levelCount = VK_REMAINING_MIP_LEVELS\n\t\t, uint32_t _baseArrayLayer = 0\n\t\t, uint32_t _layerCount = VK_REMAINING_ARRAY_LAYERS\n\t\t)\n\t{\n\t\tBX_ASSERT(true\n\t\t\t&& _newLayout != VK_IMAGE_LAYOUT_UNDEFINED\n\t\t\t&& _newLayout != VK_IMAGE_LAYOUT_PREINITIALIZED\n\t\t\t, "_newLayout cannot use VK_IMAGE_LAYOUT_UNDEFINED or VK_IMAGE_LAYOUT_PREINITIALIZED."\n\t\t\t);\n\n\t\tconstexpr VkPipelineStageFlags depthStageMask = 0\n\t\t\t| VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT\n\t\t\t| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT\n\t\t\t;\n\n\t\tconstexpr VkPipelineStageFlags sampledStageMask = 0\n\t\t\t| VK_PIPELINE_STAGE_VERTEX_SHADER_BIT\n\t\t\t| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT\n\t\t\t| VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT\n\t\t\t;\n\n\t\tVkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;\n\t\tVkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;\n\n\t\tVkAccessFlags srcAccessMask = 0;\n\t\tVkAccessFlags dstAccessMask = 0;\n\n\t\tswitch (_oldLayout)\n\t\t{\n\t\tcase VK_IMAGE_LAYOUT_UNDEFINED:\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_GENERAL:\n\t\t\tsrcStageMask  = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;\n\t\t\tsrcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:\n\t\t\tsrcStageMask  = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n\t\t\tsrcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:\n\t\t\tsrcStageMask  = depthStageMask;\n\t\t\tsrcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:\n\t\t\tsrcStageMask = depthStageMask | sampledStageMask;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:\n\t\t\tsrcStageMask = sampledStageMask;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:\n\t\t\tsrcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:\n\t\t\tsrcStageMask  = VK_PIPELINE_STAGE_TRANSFER_BIT;\n\t\t\tsrcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_PREINITIALIZED:\n\t\t\tsrcStageMask  = VK_PIPELINE_STAGE_HOST_BIT;\n\t\t\tsrcAccessMask = VK_ACCESS_HOST_WRITE_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tBX_ASSERT(false, "Unknown image layout.");\n\t\t\tbreak;\n\t\t}\n\n\t\tswitch (_newLayout)\n\t\t{\n\t\tcase VK_IMAGE_LAYOUT_GENERAL:\n\t\t\tdstStageMask  = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;\n\t\t\tdstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:\n\t\t\tdstStageMask  = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n\t\t\tdstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:\n\t\t\tdstStageMask  = depthStageMask;\n\t\t\tdstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:\n\t\t\tdstStageMask  = depthStageMask | sampledStageMask;\n\t\t\tdstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:\n\t\t\tdstStageMask  = sampledStageMask;\n\t\t\tdstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:\n\t\t\tdstStageMask  = VK_PIPELINE_STAGE_TRANSFER_BIT;\n\t\t\tdstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:\n\t\t\tdstStageMask  = VK_PIPELINE_STAGE_TRANSFER_BIT;\n\t\t\tdstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n\t\t\tbreak;\n\n\t\tcase VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:\n\t\t\t// vkQueuePresentKHR performs automatic visibility operations\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tBX_ASSERT(false, "Unknown image layout.");\n\t\t\tbreak;\n\t\t}\n\n\t\tVkImageMemoryBarrier imb;\n\t\timb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n\t\timb.pNext = NULL;\n\t\timb.srcAccessMask = srcAccessMask;\n\t\timb.dstAccessMask = dstAccessMask;\n\t\timb.oldLayout = _oldLayout;\n\t\timb.newLayout = _newLayout;\n\t\timb.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\t\timb.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n\t\timb.image = _image;\n\t\timb.subresourceRange.aspectMask     = _aspectMask;\n\t\timb.subresourceRange.baseMipLevel   = _baseMipLevel;\n\t\timb.subresourceRange.levelCount     = _levelCount;\n\t\timb.subresourceRange.baseArrayLayer = _baseArrayLayer;\n\t\timb.subresourceRange.layerCount     = _layerCount;\n\t\tvkCmdPipelineBarrier(_commandBuffer\n\t\t\t, srcStageMask\n\t\t\t, dstStageMask\n\t\t\t, 0\n\t\t\t, 0\n\t\t\t, NULL\n\t\t\t, 0\n\t\t\t, NULL\n\t\t\t, 1\n\t\t\t, &imb\n\t\t\t);\n\t}\n\n#define MAX_DESCRIPTOR_SETS (1024 * BGFX_CONFIG_MAX_FRAME_LATENCY)\n\n\tstruct RendererContextVK : public RendererContextI\n\t{\n\t\tRendererContextVK()\n\t\t\t: m_allocatorCb(NULL)\n\t\t\t, m_renderDocDll(NULL)\n\t\t\t, m_vulkan1Dll(NULL)\n\t\t\t, m_maxAnisotropy(1.0f)\n\t\t\t, m_depthClamp(false)\n\t\t\t, m_wireframe(false)\n\t\t\t, m_captureBuffer(VK_NULL_HANDLE)\n\t\t\t, m_captureMemory(VK_NULL_HANDLE)\n\t\t\t, m_captureSize(0)\n\t\t{\n\t\t}\n\n\t\t~RendererContextVK()\n\t\t{\n\t\t}\n\n\t\tbool init(const Init& _init)\n\t\t{\n\t\t\tstruct ErrorState\n\t\t\t{\n\t\t\t\tenum Enum\n\t\t\t\t{\n\t\t\t\t\tDefault,\n\t\t\t\t\tLoadedVulkan1,\n\t\t\t\t\tInstanceCreated,\n\t\t\t\t\tDeviceCreated,\n\t\t\t\t\tCommandQueueCreated,\n\t\t\t\t\tSwapChainCreated,\n\t\t\t\t\tDescriptorCreated,\n\t\t\t\t\tTimerQueryCreated,\n\t\t\t\t};\n\t\t\t};\n\n\t\t\tErrorState::Enum errorState = ErrorState::Default;\n\n\t\t\tconst bool headless = NULL == g_platformData.nwh;\n\n\t\t\tconst void* nextFeatures = NULL;\n\t\t\tVkPhysicalDeviceLineRasterizationFeaturesEXT lineRasterizationFeatures;\n\t\t\tVkPhysicalDeviceCustomBorderColorFeaturesEXT customBorderColorFeatures;\n\n\t\t\tbx::memSet(&lineRasterizationFeatures, 0, sizeof(lineRasterizationFeatures) );\n\t\t\tbx::memSet(&customBorderColorFeatures, 0, sizeof(customBorderColorFeatures) );\n\n\t\t\tm_fbh.idx = kInvalidHandle;\n\t\t\tbx::memSet(m_uniforms, 0, sizeof(m_uniforms) );\n\t\t\tbx::memSet(&m_resolution, 0, sizeof(m_resolution) );\n\n\t\t\tbool imported = true;\n\t\t\tVkResult result;\n\t\t\tm_globalQueueFamily = UINT32_MAX;\n\n\t\t\tif (_init.debug\n\t\t\t||  _init.profile)\n\t\t\t{\n\t\t\t\tm_renderDocDll = loadRenderDoc();\n\t\t\t}\n\n\t\t\tsetGraphicsDebuggerPresent(NULL != m_renderDocDll);\n\n\t\t\tm_vulkan1Dll = bx::dlopen(\n#if BX_PLATFORM_WINDOWS\n\t\t\t\t"vulkan-1.dll"\n#elif BX_PLATFORM_ANDROID\n\t\t\t\t"libvulkan.so"\n#elif BX_PLATFORM_OSX\n\t\t\t\t"libvulkan.dylib"\n#else\n\t\t\t\t"libvulkan.so.1"\n#endif // BX_PLATFORM_*\n\t\t\t\t\t);\n\n\t\t\tif (NULL == m_vulkan1Dll)\n\t\t\t{\n\t\t\t\tBX_TRACE("Init error: Failed to load vulkan dynamic library.");\n\t\t\t\tgoto error;\n\t\t\t}\n\n\t\t\terrorState = ErrorState::LoadedVulkan1;\n\n\t\t\tBX_TRACE("Shared library functions:");\n\n#define VK_IMPORT_FUNC(_optional, _func)                  \\\n\t_func = (PFN_##_func)bx::dlsym(m_vulkan1Dll, #_func); \\\n\tBX_TRACE("\\t%p " #_func, _func);                      \\\n\timported &= _optional || NULL != _func\n\nVK_IMPORT\n\n#undef VK_IMPORT_FUNC\n\n\t\t\tif (!imported)\n\t\t\t{\n\t\t\t\tBX_TRACE("Init error: Failed to load shared library functions.");\n\t\t\t\tgoto error;\n\t\t\t}\n\n\t\t\t{\n\t\t\t\ts_layer[Layer::VK_LAYER_LUNARG_standard_validation].m_device.m_initialize   = _init.debug;\n\t\t\t\ts_layer[Layer::VK_LAYER_LUNARG_standard_validation].m_instance.m_initialize = _init.debug;\n\t\t\t\ts_layer[Layer::VK_LAYER_KHRONOS_validation        ].m_device.m_initialize   = _init.debug;\n\t\t\t\ts_layer[Layer::VK_LAYER_KHRONOS_validation        ].m_instance.m_initialize = _init.debug;\n\n\t\t\t\ts_extension[Extension::EXT_debug_report].m_initialize = _init.debug;\n\n\t\t\t\ts_extension[Extension::EXT_shader_viewport_index_layer].m_initialize = !!(_init.capabilities & BGFX_CAPS_VIEWPORT_LAYER_ARRAY);\n\t\t\t\ts_extension[Extension::EXT_conservative_rasterization ].m_initialize = !!(_init.capabilities & BGFX_CAPS_CONSERVATIVE_RASTER );\n\t\t\t\ts_extension[Extension::KHR_draw_indirect_count        ].m_initialize = !!(_init.capabilities & BGFX_CAPS_DRAW_INDIRECT_COUNT );\n\n\t\t\t\tdumpExtensions(VK_NULL_HANDLE, s_extension);\n\n\t\t\t\tif (s_layer[Layer::VK_LAYER_KHRONOS_validation].m_device.m_supported\n\t\t\t\t||  s_layer[Layer::VK_LAYER_KHRONOS_validation].m_instance.m_supported)\n\t\t\t\t{\n\t\t\t\t\ts_layer[Layer::VK_LAYER_LUNARG_standard_validation].m_device.m_supported   = false;\n\t\t\t\t\ts_layer[Layer::VK_LAYER_LUNARG_standard_validation].m_instance.m_supported = false;\n\t\t\t\t}\n\n\t\t\t\tuint32_t numEnabledLayers = 0;\n\n\t\t\t\tconst char* enabledLayer[Layer::Count];\n\n\t\t\t\tBX_TRACE("Enabled instance layers:");\n\n\t\t\t\tfor (uint32_t ii = 0; ii < Layer::Count; ++ii)\n\t\t\t\t{\n\t\t\t\t\tconst Layer& layer = s_layer[ii];\n\n\t\t\t\t\tif (layer.m_instance.m_supported\n\t\t\t\t\t&&  layer.m_instance.m_initialize)\n\t\t\t\t\t{\n\t\t\t\t\t\tenabledLayer[numEnabledLayers++] = layer.m_name;\n\t\t\t\t\t\tBX_TRACE("\\t%s", layer.m_name);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tuint32_t numEnabledExtensions = headless ? 0 : 2;\n\n\t\t\t\tconst char* enabledExtension[Extension::Count + 2] =\n\t\t\t\t{\n\t\t\t\t\tVK_KHR_SURFACE_EXTENSION_NAME,\n\t\t\t\t\tKHR_SURFACE_EXTENSION_NAME,\n\t\t\t\t};\n\n\t\t\t\tfor (uint32_t ii = 0; ii < Extension::Count; ++ii)\n\t\t\t\t{\n\t\t\t\t\tconst Extension& extension = s_extension[ii];\n\t\t\t\t\tconst LayerInfo& layerInfo = s_layer[extension.m_layer].m_instance;\n\n\t\t\t\t\tconst bool layerEnabled = false\n\t\t\t\t\t\t|| extension.m_layer == Layer::Count\n\t\t\t\t\t\t|| (layerInfo.m_supported && layerInfo.m_initialize)\n\t\t\t\t\t\t;\n\n\t\t\t\t\tif (extension.m_supported\n\t\t\t\t\t&&  extension.m_initialize\n\t\t\t\t\t&&  extension.m_instanceExt\n\t\t\t\t\t&&  layerEnabled)\n\t\t\t\t\t{\n\t\t\t\t\t\tenabledExtension[numEnabledExtensions++] = extension.m_name;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tBX_TRACE("Enabled instance extensions:");\n\n\t\t\t\tfor (uint32_t ii = 0; ii < numEnabledExtensions; ++ii)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("\\t%s", enabledExtension[ii]);\n\t\t\t\t}\n\n\t\t\t\tuint32_t vulkanApiVersionSelector;\n\n\t\t\t\tif (NULL != vkEnumerateInstanceVersion)\n\t\t\t\t{\n\t\t\t\t\tresult = vkEnumerateInstanceVersion(&vulkanApiVersionSelector);\n\n\t\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t\t{\n\t\t\t\t\t\tBX_TRACE(\n\t\t\t\t\t\t\t  "Init error: vkEnumerateInstanceVersion failed %d: %s."\n\t\t\t\t\t\t\t, result\n\t\t\t\t\t\t\t, getName(result)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvulkanApiVersionSelector = VK_API_VERSION_1_0;\n\t\t\t\t}\n\n\t\t\t\tVkApplicationInfo appInfo;\n\t\t\t\tappInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;\n\t\t\t\tappInfo.pNext = NULL;\n\t\t\t\tappInfo.pApplicationName   = "bgfx";\n\t\t\t\tappInfo.applicationVersion = BGFX_API_VERSION;\n\t\t\t\tappInfo.pEngineName        = "bgfx";\n\t\t\t\tappInfo.engineVersion      = BGFX_API_VERSION;\n\t\t\t\tappInfo.apiVersion         = vulkanApiVersionSelector;\n\n\t\t\t\tVkInstanceCreateInfo ici;\n\t\t\t\tici.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;\n\t\t\t\tici.pNext = NULL;\n\t\t\t\tici.flags = 0;\n\t\t\t\tici.pApplicationInfo        = &appInfo;\n\t\t\t\tici.enabledLayerCount       = numEnabledLayers;\n\t\t\t\tici.ppEnabledLayerNames     = enabledLayer;\n\t\t\t\tici.enabledExtensionCount   = numEnabledExtensions;\n\t\t\t\tici.ppEnabledExtensionNames = enabledExtension;\n\n\t\t\t\tif (BX_ENABLED(BGFX_CONFIG_DEBUG) )\n\t\t\t\t{\n\t\t\t\t\tm_allocatorCb = &s_allocationCb;\n\t\t\t\t\tBX_UNUSED(s_allocationCb);\n\t\t\t\t}\n\n\t\t\t\tresult = vkCreateInstance(\n\t\t\t\t\t  &ici\n\t\t\t\t\t, m_allocatorCb\n\t\t\t\t\t, &m_instance\n\t\t\t\t\t);\n\n\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Init error: vkCreateInstance failed %d: %s.", result, getName(result) );\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\n\t\t\t\tm_instanceApiVersion = vulkanApiVersionSelector;\n\n\t\t\t\tBX_TRACE("Instance API version: %d.%d.%d"\n\t\t\t\t\t, VK_API_VERSION_MAJOR(m_instanceApiVersion)\n\t\t\t\t\t, VK_API_VERSION_MINOR(m_instanceApiVersion)\n\t\t\t\t\t, VK_API_VERSION_PATCH(m_instanceApiVersion)\n\t\t\t\t\t);\n\t\t\t\tBX_TRACE("Instance variant: %d", VK_API_VERSION_VARIANT(m_instanceApiVersion) );\n\t\t\t}\n\n\t\t\terrorState = ErrorState::InstanceCreated;\n\n\t\t\tBX_TRACE("Instance functions:");\n\n#define VK_IMPORT_INSTANCE_FUNC(_optional, _func)                           \\\n\t\t\t_func = (PFN_##_func)vkGetInstanceProcAddr(m_instance, #_func); \\\n\t\t\tBX_TRACE("\\t%p " #_func, _func);                                \\\n\t\t\timported &= _optional || NULL != _func\nVK_IMPORT_INSTANCE\n#undef VK_IMPORT_INSTANCE_FUNC\n\n\t\t\tif (!imported)\n\t\t\t{\n\t\t\t\tBX_TRACE("Init error: Failed to load instance functions.");\n\t\t\t\tgoto error;\n\t\t\t}\n\n\t\t\tm_debugReportCallback = VK_NULL_HANDLE;\n\n\t\t\tif (s_extension[Extension::EXT_debug_report].m_supported)\n\t\t\t{\n\t\t\t\tVkDebugReportCallbackCreateInfoEXT drcb;\n\t\t\t\tdrcb.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;\n\t\t\t\tdrcb.pNext = NULL;\n\t\t\t\tdrcb.pfnCallback = debugReportCb;\n\t\t\t\tdrcb.pUserData   = NULL;\n\t\t\t\tdrcb.flags       = 0\n\t\t\t\t\t| VK_DEBUG_REPORT_ERROR_BIT_EXT\n\t\t\t\t\t| VK_DEBUG_REPORT_WARNING_BIT_EXT\n\t\t\t\t\t;\n\t\t\t\tresult = vkCreateDebugReportCallbackEXT(m_instance\n\t\t\t\t\t, &drcb\n\t\t\t\t\t, m_allocatorCb\n\t\t\t\t\t, &m_debugReportCallback\n\t\t\t\t\t);\n\t\t\t\tBX_WARN(VK_SUCCESS == result, "vkCreateDebugReportCallbackEXT failed %d: %s.", result, getName(result) );\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tBX_TRACE("---");\n\n\t\t\t\tuint32_t numPhysicalDevices;\n\t\t\t\tresult = vkEnumeratePhysicalDevices(m_instance\n\t\t\t\t\t, &numPhysicalDevices\n\t\t\t\t\t, NULL\n\t\t\t\t\t);\n\n\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Init error: vkEnumeratePhysicalDevices failed %d: %s.", result, getName(result) );\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\n\t\t\t\tVkPhysicalDevice physicalDevices[4];\n\t\t\t\tnumPhysicalDevices = bx::min<uint32_t>(numPhysicalDevices, BX_COUNTOF(physicalDevices) );\n\t\t\t\tresult = vkEnumeratePhysicalDevices(m_instance\n\t\t\t\t\t, &numPhysicalDevices\n\t\t\t\t\t, physicalDevices\n\t\t\t\t\t);\n\n\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Init error: vkEnumeratePhysicalDevices failed %d: %s.", result, getName(result) );\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\n\t\t\t\tExtension physicalDeviceExtensions[4][Extension::Count];\n\n\t\t\t\tuint32_t physicalDeviceIdx         = UINT32_MAX;\n\t\t\t\tuint32_t fallbackPhysicalDeviceIdx = UINT32_MAX;\n\n\t\t\t\tfor (uint32_t ii = 0; ii < numPhysicalDevices; ++ii)\n\t\t\t\t{\n\t\t\t\t\tVkPhysicalDeviceProperties pdp;\n\t\t\t\t\tvkGetPhysicalDeviceProperties(physicalDevices[ii], &pdp);\n\t\t\t\t\tBX_TRACE("Physical device %d:", ii);\n\t\t\t\t\tBX_TRACE("\\t          Name: %s", pdp.deviceName);\n\t\t\t\t\tBX_TRACE("\\t   API version: %d.%d.%d"\n\t\t\t\t\t\t, VK_API_VERSION_MAJOR(pdp.apiVersion)\n\t\t\t\t\t\t, VK_API_VERSION_MINOR(pdp.apiVersion)\n\t\t\t\t\t\t, VK_API_VERSION_PATCH(pdp.apiVersion)\n\t\t\t\t\t\t);\n\t\t\t\t\tBX_TRACE("\\t   API variant: %d", VK_API_VERSION_VARIANT(pdp.apiVersion) );\n\t\t\t\t\tBX_TRACE("\\tDriver version: %x", pdp.driverVersion);\n\t\t\t\t\tBX_TRACE("\\t      VendorId: %x", pdp.vendorID);\n\t\t\t\t\tBX_TRACE("\\t      DeviceId: %x", pdp.deviceID);\n\t\t\t\t\tBX_TRACE("\\t          Type: %d", pdp.deviceType);\n\n\t\t\t\t\tg_caps.gpu[ii].vendorId = uint16_t(pdp.vendorID);\n\t\t\t\t\tg_caps.gpu[ii].deviceId = uint16_t(pdp.deviceID);\n\t\t\t\t\t++g_caps.numGPUs;\n\n\t\t\t\t\tif ( (BGFX_PCI_ID_NONE != g_caps.vendorId ||            0 != g_caps.deviceId)\n\t\t\t\t\t&&   (BGFX_PCI_ID_NONE == g_caps.vendorId || pdp.vendorID == g_caps.vendorId)\n\t\t\t\t\t&&   (0 == g_caps.deviceId                || pdp.deviceID == g_caps.deviceId) )\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pdp.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU\n\t\t\t\t\t\t||  pdp.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfallbackPhysicalDeviceIdx = ii;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tphysicalDeviceIdx = ii;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tif (pdp.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfallbackPhysicalDeviceIdx = ii;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (pdp.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tphysicalDeviceIdx = ii;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tVkPhysicalDeviceMemoryProperties pdmp;\n\t\t\t\t\tvkGetPhysicalDeviceMemoryProperties(physicalDevices[ii], &pdmp);\n\n\t\t\t\t\tBX_TRACE("\\tMemory type count: %d", pdmp.memoryTypeCount);\n\t\t\t\t\tfor (uint32_t jj = 0; jj < pdmp.memoryTypeCount; ++jj)\n\t\t\t\t\t{\n\t\t\t\t\t\tBX_TRACE("\\t%3d: flags 0x%08x, index %d"\n\t\t\t\t\t\t\t, jj\n\t\t\t\t\t\t\t, pdmp.memoryTypes[jj].propertyFlags\n\t\t\t\t\t\t\t, pdmp.memoryTypes[jj].heapIndex\n\t\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tBX_TRACE("\\tMemory heap count: %d", pdmp.memoryHeapCount);\n\t\t\t\t\tfor (uint32_t jj = 0; jj < pdmp.memoryHeapCount; ++jj)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar size[16];\n\t\t\t\t\t\tbx::prettify(size, BX_COUNTOF(size), pdmp.memoryHeaps[jj].size);\n\t\t\t\t\t\tBX_TRACE("\\t%3d: flags 0x%08x, size %10s"\n\t\t\t\t\t\t\t, jj\n\t\t\t\t\t\t\t, pdmp.memoryHeaps[jj].flags\n\t\t\t\t\t\t\t, size\n\t\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tbx::memCopy(&physicalDeviceExtensions[ii][0], &s_extension[0], sizeof(s_extension) );\n\t\t\t\t\tdumpExtensions(physicalDevices[ii], physicalDeviceExtensions[ii]);\n\t\t\t\t}\n\n\t\t\t\tif (UINT32_MAX == physicalDeviceIdx)\n\t\t\t\t{\n\t\t\t\t\tphysicalDeviceIdx = UINT32_MAX == fallbackPhysicalDeviceIdx\n\t\t\t\t\t\t? 0\n\t\t\t\t\t\t: fallbackPhysicalDeviceIdx\n\t\t\t\t\t\t;\n\t\t\t\t}\n\n\t\t\t\tm_physicalDevice = physicalDevices[physicalDeviceIdx];\n\n\t\t\t\tbx::memCopy(&s_extension[0], &physicalDeviceExtensions[physicalDeviceIdx][0], sizeof(s_extension) );\n\n\t\t\t\tvkGetPhysicalDeviceProperties(m_physicalDevice, &m_deviceProperties);\n\t\t\t\tg_caps.vendorId = uint16_t(m_deviceProperties.vendorID);\n\t\t\t\tg_caps.deviceId = uint16_t(m_deviceProperties.deviceID);\n\n\t\t\t\tBX_TRACE("Using physical device %d: %s", physicalDeviceIdx, m_deviceProperties.deviceName);\n\n\t\t\t\tVkPhysicalDeviceFeatures supportedFeatures;\n\n\t\t\t\tif (s_extension[Extension::KHR_get_physical_device_properties2].m_supported)\n\t\t\t\t{\n\t\t\t\t\tVkPhysicalDeviceFeatures2KHR deviceFeatures2;\n\t\t\t\t\tdeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;\n\t\t\t\t\tdeviceFeatures2.pNext = NULL;\n\n\t\t\t\t\tVkBaseOutStructure* next = (VkBaseOutStructure*)&deviceFeatures2;\n\n\t\t\t\t\tif (s_extension[Extension::EXT_line_rasterization].m_supported)\n\t\t\t\t\t{\n\t\t\t\t\t\tnext->pNext = (VkBaseOutStructure*)&lineRasterizationFeatures;\n\t\t\t\t\t\tnext = (VkBaseOutStructure*)&lineRasterizationFeatures;\n\t\t\t\t\t\tlineRasterizationFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT;\n\t\t\t\t\t\tlineRasterizationFeatures.pNext = NULL;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (s_extension[Extension::EXT_custom_border_color].m_supported)\n\t\t\t\t\t{\n\t\t\t\t\t\tnext->pNext = (VkBaseOutStructure*)&customBorderColorFeatures;\n\t\t\t\t\t\tnext = (VkBaseOutStructure*)&customBorderColorFeatures;\n\t\t\t\t\t\tcustomBorderColorFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;\n\t\t\t\t\t\tcustomBorderColorFeatures.pNext = NULL;\n\t\t\t\t\t}\n\n\t\t\t\t\tnextFeatures = deviceFeatures2.pNext;\n\n\t\t\t\t\tvkGetPhysicalDeviceFeatures2KHR(m_physicalDevice, &deviceFeatures2);\n\t\t\t\t\tsupportedFeatures = deviceFeatures2.features;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tvkGetPhysicalDeviceFeatures(m_physicalDevice, &supportedFeatures);\n\t\t\t\t}\n\n\t\t\t\tbx::memSet(&m_deviceFeatures, 0, sizeof(m_deviceFeatures) );\n\n\t\t\t\tm_deviceFeatures.fullDrawIndexUint32       = supportedFeatures.fullDrawIndexUint32;\n\t\t\t\tm_deviceFeatures.imageCubeArray            = supportedFeatures.imageCubeArray            && (_init.capabilities & BGFX_CAPS_TEXTURE_CUBE_ARRAY);\n\t\t\t\tm_deviceFeatures.independentBlend          = supportedFeatures.independentBlend          && (_init.capabilities & BGFX_CAPS_BLEND_INDEPENDENT);\n\t\t\t\tm_deviceFeatures.multiDrawIndirect         = supportedFeatures.multiDrawIndirect         && (_init.capabilities & BGFX_CAPS_DRAW_INDIRECT);\n\t\t\t\tm_deviceFeatures.drawIndirectFirstInstance = supportedFeatures.drawIndirectFirstInstance && (_init.capabilities & BGFX_CAPS_DRAW_INDIRECT);\n\t\t\t\tm_deviceFeatures.depthClamp        = supportedFeatures.depthClamp;\n\t\t\t\tm_deviceFeatures.fillModeNonSolid  = supportedFeatures.fillModeNonSolid;\n\t\t\t\tm_deviceFeatures.largePoints       = supportedFeatures.largePoints;\n\t\t\t\tm_deviceFeatures.samplerAnisotropy = supportedFeatures.samplerAnisotropy;\n\t\t\t\tm_deviceFeatures.textureCompressionETC2 = supportedFeatures.textureCompressionETC2;\n\t\t\t\tm_deviceFeatures.textureCompressionBC   = supportedFeatures.textureCompressionBC;\n\t\t\t\tm_deviceFeatures.vertexPipelineStoresAndAtomics = supportedFeatures.vertexPipelineStoresAndAtomics;\n\t\t\t\tm_deviceFeatures.fragmentStoresAndAtomics  = supportedFeatures.fragmentStoresAndAtomics;\n\t\t\t\tm_deviceFeatures.shaderImageGatherExtended = supportedFeatures.shaderImageGatherExtended;\n\t\t\t\tm_deviceFeatures.shaderStorageImageExtendedFormats = supportedFeatures.shaderStorageImageExtendedFormats;\n\t\t\t\tm_deviceFeatures.shaderClipDistance   = supportedFeatures.shaderClipDistance;\n\t\t\t\tm_deviceFeatures.shaderCullDistance   = supportedFeatures.shaderCullDistance;\n\t\t\t\tm_deviceFeatures.shaderResourceMinLod = supportedFeatures.shaderResourceMinLod;\n\n\t\t\t\tm_lineAASupport = true\n\t\t\t\t\t&& s_extension[Extension::EXT_line_rasterization].m_supported\n\t\t\t\t\t&& lineRasterizationFeatures.smoothLines\n\t\t\t\t\t;\n\n\t\t\t\tm_borderColorSupport = true\n\t\t\t\t\t&& s_extension[Extension::EXT_custom_border_color].m_supported\n\t\t\t\t\t&& customBorderColorFeatures.customBorderColors\n\t\t\t\t\t;\n\n\t\t\t\tm_timerQuerySupport = m_deviceProperties.limits.timestampComputeAndGraphics;\n\n\t\t\t\tconst bool indirectDrawSupport = true\n\t\t\t\t\t&& m_deviceFeatures.multiDrawIndirect\n\t\t\t\t\t&& m_deviceFeatures.drawIndirectFirstInstance\n\t\t\t\t\t;\n\n\t\t\t\tg_caps.supported |= ( 0\n\t\t\t\t\t| BGFX_CAPS_ALPHA_TO_COVERAGE\n\t\t\t\t\t| (m_deviceFeatures.independentBlend ? BGFX_CAPS_BLEND_INDEPENDENT : 0)\n\t\t\t\t\t| BGFX_CAPS_COMPUTE\n\t\t\t\t\t| (indirectDrawSupport ? BGFX_CAPS_DRAW_INDIRECT : 0)\n\t\t\t\t\t| BGFX_CAPS_FRAGMENT_DEPTH\n\t\t\t\t\t| BGFX_CAPS_IMAGE_RW\n\t\t\t\t\t| (m_deviceFeatures.fullDrawIndexUint32 ? BGFX_CAPS_INDEX32 : 0)\n\t\t\t\t\t| BGFX_CAPS_INSTANCING\n\t\t\t\t\t| BGFX_CAPS_OCCLUSION_QUERY\n\t\t\t\t\t| (!headless ? BGFX_CAPS_SWAP_CHAIN : 0)\n\t\t\t\t\t| BGFX_CAPS_TEXTURE_2D_ARRAY\n\t\t\t\t\t| BGFX_CAPS_TEXTURE_3D\n\t\t\t\t\t| BGFX_CAPS_TEXTURE_BLIT\n\t\t\t\t\t| BGFX_CAPS_TEXTURE_COMPARE_ALL\n\t\t\t\t\t| (m_deviceFeatures.imageCubeArray ? BGFX_CAPS_TEXTURE_CUBE_ARRAY : 0)\n\t\t\t\t\t| BGFX_CAPS_TEXTURE_READ_BACK\n\t\t\t\t\t| BGFX_CAPS_VERTEX_ATTRIB_HALF\n\t\t\t\t\t| BGFX_CAPS_VERTEX_ATTRIB_UINT10\n\t\t\t\t\t| BGFX_CAPS_VERTEX_ID\n\t\t\t\t\t);\n\n\t\t\t\tg_caps.supported |= 0\n\t\t\t\t\t| (s_extension[Extension::EXT_conservative_rasterization ].m_supported ? BGFX_CAPS_CONSERVATIVE_RASTER  : 0)\n\t\t\t\t\t| (s_extension[Extension::EXT_shader_viewport_index_layer].m_supported ? BGFX_CAPS_VIEWPORT_LAYER_ARRAY : 0)\n\t\t\t\t\t| (s_extension[Extension::KHR_draw_indirect_count        ].m_supported && indirectDrawSupport ? BGFX_CAPS_DRAW_INDIRECT_COUNT : 0)\n\t\t\t\t\t;\n\n\t\t\t\tconst uint32_t maxAttachments = bx::min<uint32_t>(m_deviceProperties.limits.maxFragmentOutputAttachments, m_deviceProperties.limits.maxColorAttachments);\n\n\t\t\t\tg_caps.limits.maxTextureSize     = m_deviceProperties.limits.maxImageDimension2D;\n\t\t\t\tg_caps.limits.maxTextureLayers   = m_deviceProperties.limits.maxImageArrayLayers;\n\t\t\t\tg_caps.limits.maxFBAttachments   = bx::min<uint32_t>(maxAttachments, BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS);\n\t\t\t\tg_caps.limits.maxTextureSamplers = bx::min<uint32_t>(m_deviceProperties.limits.maxPerStageResources, BGFX_CONFIG_MAX_TEXTURE_SAMPLERS);\n\t\t\t\tg_caps.limits.maxComputeBindings = bx::min<uint32_t>(m_deviceProperties.limits.maxPerStageResources, BGFX_MAX_COMPUTE_BINDINGS);\n\t\t\t\tg_caps.limits.maxVertexStreams   = bx::min<uint32_t>(m_deviceProperties.limits.maxVertexInputBindings, BGFX_CONFIG_MAX_VERTEX_STREAMS);\n\n\t\t\t\t{\n\t\t\t\t\tconst VkSampleCountFlags sampleMask = ~0\n\t\t\t\t\t\t& m_deviceProperties.limits.framebufferColorSampleCounts\n\t\t\t\t\t\t& m_deviceProperties.limits.framebufferDepthSampleCounts\n\t\t\t\t\t\t;\n\n\t\t\t\t\tfor (uint16_t ii = 0, last = 0; ii < BX_COUNTOF(s_msaa); ii++)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst VkSampleCountFlags sampleBit = s_msaa[ii].Sample;\n\n\t\t\t\t\t\tif (sampleBit & sampleMask)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlast = ii;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ts_msaa[ii] = s_msaa[last];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t{\n\t\t\t\t\tstruct ImageTest\n\t\t\t\t\t{\n\t\t\t\t\t\tVkImageType        type;\n\t\t\t\t\t\tVkImageUsageFlags  usage;\n\t\t\t\t\t\tVkImageCreateFlags flags;\n\t\t\t\t\t\tuint32_t           formatCaps[2];\n\t\t\t\t\t};\n\n\t\t\t\t\tconst ImageTest imageTest[] =\n\t\t\t\t\t{\n\t\t\t\t\t\t{ VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_SAMPLED_BIT,                  0,                                   { BGFX_CAPS_FORMAT_TEXTURE_2D,          BGFX_CAPS_FORMAT_TEXTURE_2D_SRGB   } },\n\t\t\t\t\t\t{ VK_IMAGE_TYPE_3D, VK_IMAGE_USAGE_SAMPLED_BIT,                  0,                                   { BGFX_CAPS_FORMAT_TEXTURE_3D,          BGFX_CAPS_FORMAT_TEXTURE_3D_SRGB   } },\n\t\t\t\t\t\t{ VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_SAMPLED_BIT,                  VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, { BGFX_CAPS_FORMAT_TEXTURE_CUBE,        BGFX_CAPS_FORMAT_TEXTURE_CUBE_SRGB } },\n\t\t\t\t\t\t{ VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,         0,                                   { BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER, 0                                  } },\n\t\t\t\t\t\t{ VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0,                                   { BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER, 0                                  } },\n\t\t\t\t\t\t{ VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_STORAGE_BIT,                  0,                                   { BGFX_CAPS_FORMAT_TEXTURE_IMAGE_READ,  0                                  } },\n\t\t\t\t\t\t{ VK_IMAGE_TYPE_2D, VK_IMAGE_USAGE_STORAGE_BIT,                  0,                                   { BGFX_CAPS_FORMAT_TEXTURE_IMAGE_WRITE, 0                                  } },\n\t\t\t\t\t};\n\n\t\t\t\t\tfor (uint32_t ii = 0; ii < TextureFormat::Count; ++ii)\n\t\t\t\t\t{\n\t\t\t\t\t\tuint16_t support = BGFX_CAPS_FORMAT_TEXTURE_NONE;\n\n\t\t\t\t\t\tconst bool depth = bimg::isDepth(bimg::TextureFormat::Enum(ii) );\n\t\t\t\t\t\tVkFormat fmt = depth\n\t\t\t\t\t\t\t? s_textureFormat[ii].m_fmtDsv\n\t\t\t\t\t\t\t: s_textureFormat[ii].m_fmt\n\t\t\t\t\t\t\t;\n\n\t\t\t\t\t\tfor (uint32_t jj = 0, num = depth ? 1 : 2; jj < num; ++jj)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (VK_FORMAT_UNDEFINED != fmt)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfor (uint32_t test = 0; test < BX_COUNTOF(imageTest); ++test)\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tconst ImageTest& it = imageTest[test];\n\n\t\t\t\t\t\t\t\t\tVkImageFormatProperties ifp;\n\t\t\t\t\t\t\t\t\tresult = vkGetPhysicalDeviceImageFormatProperties(m_physicalDevice\n\t\t\t\t\t\t\t\t\t\t, fmt\n\t\t\t\t\t\t\t\t\t\t, it.type\n\t\t\t\t\t\t\t\t\t\t, VK_IMAGE_TILING_OPTIMAL\n\t\t\t\t\t\t\t\t\t\t, it.usage\n\t\t\t\t\t\t\t\t\t\t, it.flags\n\t\t\t\t\t\t\t\t\t\t, &ifp\n\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\tif (VK_SUCCESS == result)\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tsupport |= it.formatCaps[jj];\n\n\t\t\t\t\t\t\t\t\t\tconst bool multisample = VK_SAMPLE_COUNT_1_BIT < ifp.sampleCounts;\n\t\t\t\t\t\t\t\t\t\tif (it.usage & VK_IMAGE_USAGE_SAMPLED_BIT)\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tsupport |= 0\n\t\t\t\t\t\t\t\t\t\t\t\t| BGFX_CAPS_FORMAT_TEXTURE_VERTEX\n\t\t\t\t\t\t\t\t\t\t\t\t| (multisample ? BGFX_CAPS_FORMAT_TEXTURE_MSAA : 0)\n\t\t\t\t\t\t\t\t\t\t\t\t;\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tif (it.usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) )\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tsupport |= 0\n\t\t\t\t\t\t\t\t\t\t\t\t| BGFX_CAPS_FORMAT_TEXTURE_MIP_AUTOGEN\n\t\t\t\t\t\t\t\t\t\t\t\t| (multisample ? BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA : 0)\n\t\t\t\t\t\t\t\t\t\t\t\t;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfmt = s_textureFormat[ii].m_fmtSrgb;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tg_caps.formats[ii] = support;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tvkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &m_memoryProperties);\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tBX_TRACE("---");\n\n\t\t\t\tuint32_t queueFamilyPropertyCount = 0;\n\t\t\t\tvkGetPhysicalDeviceQueueFamilyProperties(\n\t\t\t\t\t  m_physicalDevice\n\t\t\t\t\t, &queueFamilyPropertyCount\n\t\t\t\t\t, NULL\n\t\t\t\t\t);\n\n\t\t\t\tVkQueueFamilyProperties* queueFamilyPropertices = (VkQueueFamilyProperties*)BX_ALLOC(g_allocator, queueFamilyPropertyCount * sizeof(VkQueueFamilyProperties) );\n\t\t\t\tvkGetPhysicalDeviceQueueFamilyProperties(\n\t\t\t\t\t  m_physicalDevice\n\t\t\t\t\t, &queueFamilyPropertyCount\n\t\t\t\t\t, queueFamilyPropertices\n\t\t\t\t\t);\n\n\t\t\t\tfor (uint32_t ii = 0; ii < queueFamilyPropertyCount; ++ii)\n\t\t\t\t{\n\t\t\t\t\tconst VkQueueFamilyProperties& qfp = queueFamilyPropertices[ii];\n\n\t\t\t\t\tBX_TRACE("Queue family property %d:", ii);\n\t\t\t\t\tBX_TRACE("\\t  Queue flags: 0x%08x", qfp.queueFlags);\n\t\t\t\t\tBX_TRACE("\\t  Queue count: %d", qfp.queueCount);\n\t\t\t\t\tBX_TRACE("\\tTS valid bits: 0x%08x", qfp.timestampValidBits);\n\t\t\t\t\tBX_TRACE("\\t    Min image: %d x %d x %d"\n\t\t\t\t\t\t, qfp.minImageTransferGranularity.width\n\t\t\t\t\t\t, qfp.minImageTransferGranularity.height\n\t\t\t\t\t\t, qfp.minImageTransferGranularity.depth\n\t\t\t\t\t\t);\n\n\t\t\t\t\tconstexpr VkQueueFlags requiredFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;\n\n\t\t\t\t\tif (UINT32_MAX == m_globalQueueFamily\n\t\t\t\t\t&&  requiredFlags == (requiredFlags & qfp.queueFlags) )\n\t\t\t\t\t{\n\t\t\t\t\t\tm_globalQueueFamily = ii;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tBX_FREE(g_allocator, queueFamilyPropertices);\n\n\t\t\t\tif (UINT32_MAX == m_globalQueueFamily)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Init error: Unable to find combined graphics and compute queue.");\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tuint32_t numEnabledLayers = 0;\n\n\t\t\t\tconst char* enabledLayer[Layer::Count];\n\n\t\t\t\tBX_TRACE("Enabled device layers:");\n\n\t\t\t\tfor (uint32_t ii = 0; ii < Layer::Count; ++ii)\n\t\t\t\t{\n\t\t\t\t\tconst Layer& layer = s_layer[ii];\n\n\t\t\t\t\tif (layer.m_device.m_supported\n\t\t\t\t\t&&  layer.m_device.m_initialize)\n\t\t\t\t\t{\n\t\t\t\t\t\tenabledLayer[numEnabledLayers++] = layer.m_name;\n\t\t\t\t\t\tBX_TRACE("\\t%s", layer.m_name);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tuint32_t numEnabledExtensions = headless ? 1 : 2;\n\n\t\t\t\tconst char* enabledExtension[Extension::Count + 2] =\n\t\t\t\t{\n\t\t\t\t\tVK_KHR_MAINTENANCE1_EXTENSION_NAME,\n\t\t\t\t\tVK_KHR_SWAPCHAIN_EXTENSION_NAME\n\t\t\t\t};\n\n\t\t\t\tfor (uint32_t ii = 0; ii < Extension::Count; ++ii)\n\t\t\t\t{\n\t\t\t\t\tconst Extension& extension = s_extension[ii];\n\n\t\t\t\t\tbool layerEnabled = extension.m_layer == Layer::Count ||\n\t\t\t\t\t\t\t\t\t\t(s_layer[extension.m_layer].m_device.m_supported &&\n\t\t\t\t\t\t\t\t\t\t s_layer[extension.m_layer].m_device.m_initialize);\n\n\t\t\t\t\tif (extension.m_supported\n\t\t\t\t\t&&  extension.m_initialize\n\t\t\t\t\t&& !extension.m_instanceExt\n\t\t\t\t\t&&  layerEnabled)\n\t\t\t\t\t{\n\t\t\t\t\t\tenabledExtension[numEnabledExtensions++] = extension.m_name;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tBX_TRACE("Enabled device extensions:");\n\n\t\t\t\tfor (uint32_t ii = 0; ii < numEnabledExtensions; ++ii)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("\\t%s", enabledExtension[ii]);\n\t\t\t\t}\n\n\t\t\t\tfloat queuePriorities[1] = { 0.0f };\n\t\t\t\tVkDeviceQueueCreateInfo dcqi;\n\t\t\t\tdcqi.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;\n\t\t\t\tdcqi.pNext = NULL;\n\t\t\t\tdcqi.flags = 0;\n\t\t\t\tdcqi.queueFamilyIndex = m_globalQueueFamily;\n\t\t\t\tdcqi.queueCount       = 1;\n\t\t\t\tdcqi.pQueuePriorities = queuePriorities;\n\n\t\t\t\tVkDeviceCreateInfo dci;\n\t\t\t\tdci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;\n\t\t\t\tdci.pNext = nextFeatures;\n\t\t\t\tdci.flags = 0;\n\t\t\t\tdci.queueCreateInfoCount = 1;\n\t\t\t\tdci.pQueueCreateInfos    = &dcqi;\n\t\t\t\tdci.enabledLayerCount    = numEnabledLayers;\n\t\t\t\tdci.ppEnabledLayerNames  = enabledLayer;\n\t\t\t\tdci.enabledExtensionCount   = numEnabledExtensions;\n\t\t\t\tdci.ppEnabledExtensionNames = enabledExtension;\n\t\t\t\tdci.pEnabledFeatures = &m_deviceFeatures;\n\n\t\t\t\tresult = vkCreateDevice(\n\t\t\t\t\t  m_physicalDevice\n\t\t\t\t\t, &dci\n\t\t\t\t\t, m_allocatorCb\n\t\t\t\t\t, &m_device\n\t\t\t\t\t);\n\n\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Init error: vkCreateDevice failed %d: %s.", result, getName(result) );\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\terrorState = ErrorState::DeviceCreated;\n\n\t\t\tBX_TRACE("Device functions:");\n#define VK_IMPORT_DEVICE_FUNC(_optional, _func)                         \\\n\t\t\t_func = (PFN_##_func)vkGetDeviceProcAddr(m_device, #_func); \\\n\t\t\tBX_TRACE("\\t%p " #_func, _func);                            \\\n\t\t\timported &= _optional || NULL != _func\nVK_IMPORT_DEVICE\n#undef VK_IMPORT_DEVICE_FUNC\n\n\t\t\tif (!imported)\n\t\t\t{\n\t\t\t\tBX_TRACE("Init error: Failed to load device functions.");\n\t\t\t\tgoto error;\n\t\t\t}\n\n\t\t\tvkGetDeviceQueue(m_device, m_globalQueueFamily, 0, &m_globalQueue);\n\n\t\t\t{\n\t\t\t\tm_numFramesInFlight = _init.resolution.maxFrameLatency == 0\n\t\t\t\t\t? BGFX_CONFIG_MAX_FRAME_LATENCY\n\t\t\t\t\t: _init.resolution.maxFrameLatency\n\t\t\t\t\t;\n\n\t\t\t\tresult = m_cmd.init(m_globalQueueFamily, m_globalQueue, m_numFramesInFlight);\n\n\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Init error: creating command queue failed %d: %s.", result, getName(result) );\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\n\t\t\t\tresult = m_cmd.alloc(&m_commandBuffer);\n\n\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Init error: allocating command buffer failed %d: %s.", result, getName(result) );\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\terrorState = ErrorState::CommandQueueCreated;\n\n\t\t\tm_presentElapsed = 0;\n\n\t\t\t{\n\t\t\t\tm_resolution = _init.resolution;\n\t\t\t\tm_resolution.reset &= ~BGFX_RESET_INTERNAL_FORCE;\n\n\t\t\t\tm_numWindows = 0;\n\n\t\t\t\tif (!headless)\n\t\t\t\t{\n\t\t\t\t\tm_textVideoMem.resize(false, _init.resolution.width, _init.resolution.height);\n\t\t\t\t\tm_textVideoMem.clear();\n\n\t\t\t\t\tfor (uint8_t ii = 0; ii < BX_COUNTOF(m_swapchainFormats); ++ii)\n\t\t\t\t\t{\n\t\t\t\t\t\tm_swapchainFormats[ii] = TextureFormat::Enum(ii);\n\t\t\t\t\t}\n\n\t\t\t\t\tresult = m_backBuffer.create(UINT16_MAX, g_platformData.nwh, m_resolution.width, m_resolution.height, m_resolution.format);\n\n\t\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t\t{\n\t\t\t\t\t\tBX_TRACE("Init error: creating swap chain failed %d: %s.", result, getName(result) );\n\t\t\t\t\t\tgoto error;\n\t\t\t\t\t}\n\n\t\t\t\t\tm_windows[0] = BGFX_INVALID_HANDLE;\n\t\t\t\t\tm_numWindows++;\n\n\t\t\t\t\tpostReset();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\terrorState = ErrorState::SwapChainCreated;\n\n\t\t\t{\n\t\t\t\tVkDescriptorPoolSize dps[] =\n\t\t\t\t{\n\t\t\t\t\t{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,          MAX_DESCRIPTOR_SETS * BGFX_CONFIG_MAX_TEXTURE_SAMPLERS },\n\t\t\t\t\t{ VK_DESCRIPTOR_TYPE_SAMPLER,                MAX_DESCRIPTOR_SETS * BGFX_CONFIG_MAX_TEXTURE_SAMPLERS },\n\t\t\t\t\t{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, MAX_DESCRIPTOR_SETS * 2                                },\n\t\t\t\t\t{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,         MAX_DESCRIPTOR_SETS * BGFX_CONFIG_MAX_TEXTURE_SAMPLERS },\n\t\t\t\t\t{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,          MAX_DESCRIPTOR_SETS * BGFX_CONFIG_MAX_TEXTURE_SAMPLERS },\n\t\t\t\t};\n\n\t\t\t\tVkDescriptorPoolCreateInfo dpci;\n\t\t\t\tdpci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;\n\t\t\t\tdpci.pNext = NULL;\n\t\t\t\tdpci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;\n\t\t\t\tdpci.maxSets       = MAX_DESCRIPTOR_SETS;\n\t\t\t\tdpci.poolSizeCount = BX_COUNTOF(dps);\n\t\t\t\tdpci.pPoolSizes    = dps;\n\n\t\t\t\tresult = vkCreateDescriptorPool(m_device, &dpci, m_allocatorCb, &m_descriptorPool);\n\n\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Init error: vkCreateDescriptorPool failed %d: %s.", result, getName(result) );\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\n\t\t\t\tVkPipelineCacheCreateInfo pcci;\n\t\t\t\tpcci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;\n\t\t\t\tpcci.pNext = NULL;\n\t\t\t\tpcci.flags = 0;\n\t\t\t\tpcci.initialDataSize = 0;\n\t\t\t\tpcci.pInitialData    = NULL;\n\t\t\t\tresult = vkCreatePipelineCache(m_device, &pcci, m_allocatorCb, &m_pipelineCache);\n\n\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Init error: vkCreatePipelineCache failed %d: %s.", result, getName(result) );\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tconst uint32_t size = 128;\n\t\t\t\tconst uint32_t count = BGFX_CONFIG_MAX_DRAW_CALLS;\n\t\t\t\tfor (uint32_t ii = 0; ii < m_numFramesInFlight; ++ii)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Create scratch buffer %d", ii);\n\t\t\t\t\tm_scratchBuffer[ii].create(size, count);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\terrorState = ErrorState::DescriptorCreated;\n\n\t\t\tif (NULL == vkSetDebugUtilsObjectNameEXT)\n\t\t\t{\n\t\t\t\tvkSetDebugUtilsObjectNameEXT = stubSetDebugUtilsObjectNameEXT;\n\t\t\t}\n\n\t\t\tif (NULL == vkCmdBeginDebugUtilsLabelEXT\n\t\t\t||  NULL == vkCmdEndDebugUtilsLabelEXT)\n\t\t\t{\n\t\t\t\tvkCmdBeginDebugUtilsLabelEXT = stubCmdBeginDebugUtilsLabelEXT;\n\t\t\t\tvkCmdEndDebugUtilsLabelEXT   = stubCmdEndDebugUtilsLabelEXT;\n\t\t\t}\n\n\t\t\tif (NULL == vkCmdInsertDebugUtilsLabelEXT)\n\t\t\t{\n\t\t\t\tvkCmdInsertDebugUtilsLabelEXT = stubCmdInsertDebugUtilsLabelEXT;\n\t\t\t}\n\n\t\t\t// Init reserved part of view name.\n\t\t\tfor (uint32_t ii = 0; ii < BGFX_CONFIG_MAX_VIEWS; ++ii)\n\t\t\t{\n\t\t\t\tbx::snprintf(s_viewName[ii], BGFX_CONFIG_MAX_VIEW_NAME_RESERVED+1, "%3d   ", ii);\n\t\t\t}\n\n\t\t\tif (m_timerQuerySupport)\n\t\t\t{\n\t\t\t\tresult = m_gpuTimer.init();\n\n\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Init error: creating GPU timer failed %d: %s.", result, getName(result) );\n\t\t\t\t\tgoto error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\terrorState = ErrorState::TimerQueryCreated;\n\n\t\t\tresult = m_occlusionQuery.init();\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Init error: creating occlusion query failed %d: %s.", result, getName(result) );\n\t\t\t\tgoto error;\n\t\t\t}\n\n\t\t\tg_internalData.context = m_device;\n\t\t\treturn true;\n\n\t\terror:\n\t\t\tBX_TRACE("errorState %d", errorState);\n\t\t\tswitch (errorState)\n\t\t\t{\n\t\t\tcase ErrorState::TimerQueryCreated:\n\t\t\t\tif (m_timerQuerySupport)\n\t\t\t\t{\n\t\t\t\t\tm_gpuTimer.shutdown();\n\t\t\t\t}\n\t\t\t\tBX_FALLTHROUGH;\n\n\t\t\tcase ErrorState::DescriptorCreated:\n\t\t\t\tfor (uint32_t ii = 0; ii < m_numFramesInFlight; ++ii)\n\t\t\t\t{\n\t\t\t\t\tm_scratchBuffer[ii].destroy();\n\t\t\t\t}\n\t\t\t\tvkDestroy(m_pipelineCache);\n\t\t\t\tvkDestroy(m_descriptorPool);\n\t\t\t\tBX_FALLTHROUGH;\n\n\t\t\tcase ErrorState::SwapChainCreated:\n\t\t\t\tm_backBuffer.destroy();\n\t\t\t\tBX_FALLTHROUGH;\n\n\t\t\tcase ErrorState::CommandQueueCreated:\n\t\t\t\tm_cmd.shutdown();\n\t\t\t\tBX_FALLTHROUGH;\n\n\t\t\tcase ErrorState::DeviceCreated:\n\t\t\t\tvkDestroyDevice(m_device, m_allocatorCb);\n\t\t\t\tBX_FALLTHROUGH;\n\n\t\t\tcase ErrorState::InstanceCreated:\n\t\t\t\tif (VK_NULL_HANDLE != m_debugReportCallback)\n\t\t\t\t{\n\t\t\t\t\tvkDestroyDebugReportCallbackEXT(m_instance, m_debugReportCallback, m_allocatorCb);\n\t\t\t\t}\n\n\t\t\t\tvkDestroyInstance(m_instance, m_allocatorCb);\n\t\t\t\tBX_FALLTHROUGH;\n\n\t\t\tcase ErrorState::LoadedVulkan1:\n\t\t\t\tbx::dlclose(m_vulkan1Dll);\n\t\t\t\tm_vulkan1Dll  = NULL;\n\t\t\t\tm_allocatorCb = NULL;\n\t\t\t\tunloadRenderDoc(m_renderDocDll);\n\t\t\t\tBX_FALLTHROUGH;\n\n\t\t\tcase ErrorState::Default:\n\t\t\t\tbreak;\n\t\t\t};\n\n\t\t\treturn false;\n\t\t}\n\n\t\tvoid shutdown()\n\t\t{\n\t\t\tVK_CHECK(vkDeviceWaitIdle(m_device) );\n\n\t\t\tif (m_timerQuerySupport)\n\t\t\t{\n\t\t\t\tm_gpuTimer.shutdown();\n\t\t\t}\n\t\t\tm_occlusionQuery.shutdown();\n\n\t\t\tpreReset();\n\n\t\t\tm_pipelineStateCache.invalidate();\n\t\t\tm_descriptorSetLayoutCache.invalidate();\n\t\t\tm_renderPassCache.invalidate();\n\t\t\tm_samplerCache.invalidate();\n\t\t\tm_samplerBorderColorCache.invalidate();\n\t\t\tm_imageViewCache.invalidate();\n\n\t\t\tfor (uint32_t ii = 0; ii < m_numFramesInFlight; ++ii)\n\t\t\t{\n\t\t\t\tm_scratchBuffer[ii].destroy();\n\t\t\t}\n\n\t\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_frameBuffers); ++ii)\n\t\t\t{\n\t\t\t\tm_frameBuffers[ii].destroy();\n\t\t\t}\n\n\t\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_indexBuffers); ++ii)\n\t\t\t{\n\t\t\t\tm_indexBuffers[ii].destroy();\n\t\t\t}\n\n\t\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_vertexBuffers); ++ii)\n\t\t\t{\n\t\t\t\tm_vertexBuffers[ii].destroy();\n\t\t\t}\n\n\t\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_shaders); ++ii)\n\t\t\t{\n\t\t\t\tm_shaders[ii].destroy();\n\t\t\t}\n\n\t\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_textures); ++ii)\n\t\t\t{\n\t\t\t\tm_textures[ii].destroy();\n\t\t\t}\n\n\t\t\tm_backBuffer.destroy();\n\n\t\t\tm_cmd.shutdown();\n\n\t\t\tvkDestroy(m_pipelineCache);\n\t\t\tvkDestroy(m_descriptorPool);\n\n\t\t\tvkDestroyDevice(m_device, m_allocatorCb);\n\n\t\t\tif (VK_NULL_HANDLE != m_debugReportCallback)\n\t\t\t{\n\t\t\t\tvkDestroyDebugReportCallbackEXT(m_instance, m_debugReportCallback, m_allocatorCb);\n\t\t\t}\n\n\t\t\tvkDestroyInstance(m_instance, m_allocatorCb);\n\n\t\t\tbx::dlclose(m_vulkan1Dll);\n\t\t\tm_vulkan1Dll  = NULL;\n\t\t\tm_allocatorCb = NULL;\n\t\t\tunloadRenderDoc(m_renderDocDll);\n\t\t}\n\n\t\tRendererType::Enum getRendererType() const override\n\t\t{\n\t\t\treturn RendererType::Vulkan;\n\t\t}\n\n\t\tconst char* getRendererName() const override\n\t\t{\n\t\t\treturn BGFX_RENDERER_VULKAN_NAME;\n\t\t}\n\n\t\tbool isDeviceRemoved() override\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tvoid flip() override\n\t\t{\n\t\t\tint64_t start = bx::getHPCounter();\n\n\t\t\tfor (uint16_t ii = 0; ii < m_numWindows; ++ii)\n\t\t\t{\n\t\t\t\tFrameBufferVK& fb = isValid(m_windows[ii])\n\t\t\t\t\t? m_frameBuffers[m_windows[ii].idx]\n\t\t\t\t\t: m_backBuffer\n\t\t\t\t\t;\n\n\t\t\t\tfb.present();\n\t\t\t}\n\n\t\t\tint64_t now = bx::getHPCounter();\n\n\t\t\tm_presentElapsed += now - start;\n\t\t}\n\n\t\tvoid createIndexBuffer(IndexBufferHandle _handle, const Memory* _mem, uint16_t _flags) override\n\t\t{\n\t\t\tm_indexBuffers[_handle.idx].create(m_commandBuffer, _mem->size, _mem->data, _flags, false);\n\t\t}\n\n\t\tvoid destroyIndexBuffer(IndexBufferHandle _handle) override\n\t\t{\n\t\t\tm_indexBuffers[_handle.idx].destroy();\n\t\t}\n\n\t\tvoid createVertexLayout(VertexLayoutHandle _handle, const VertexLayout& _layout) override\n\t\t{\n\t\t\tVertexLayout& layout = m_vertexLayouts[_handle.idx];\n\t\t\tbx::memCopy(&layout, &_layout, sizeof(VertexLayout) );\n\t\t\tdump(layout);\n\t\t}\n\n\t\tvoid destroyVertexLayout(VertexLayoutHandle /*_handle*/) override\n\t\t{\n\t\t}\n\n\t\tvoid createVertexBuffer(VertexBufferHandle _handle, const Memory* _mem, VertexLayoutHandle _layoutHandle, uint16_t _flags) override\n\t\t{\n\t\t\tm_vertexBuffers[_handle.idx].create(m_commandBuffer, _mem->size, _mem->data, _layoutHandle, _flags);\n\t\t}\n\n\t\tvoid destroyVertexBuffer(VertexBufferHandle _handle) override\n\t\t{\n\t\t\tm_vertexBuffers[_handle.idx].destroy();\n\t\t}\n\n\t\tvoid createDynamicIndexBuffer(IndexBufferHandle _handle, uint32_t _size, uint16_t _flags) override\n\t\t{\n\t\t\tm_indexBuffers[_handle.idx].create(m_commandBuffer, _size, NULL, _flags, false);\n\t\t}\n\n\t\tvoid updateDynamicIndexBuffer(IndexBufferHandle _handle, uint32_t _offset, uint32_t _size, const Memory* _mem) override\n\t\t{\n\t\t\tm_indexBuffers[_handle.idx].update(m_commandBuffer, _offset, bx::min<uint32_t>(_size, _mem->size), _mem->data);\n\t\t}\n\n\t\tvoid destroyDynamicIndexBuffer(IndexBufferHandle _handle) override\n\t\t{\n\t\t\tm_indexBuffers[_handle.idx].destroy();\n\t\t}\n\n\t\tvoid createDynamicVertexBuffer(VertexBufferHandle _handle, uint32_t _size, uint16_t _flags) override\n\t\t{\n\t\t\tVertexLayoutHandle layoutHandle = BGFX_INVALID_HANDLE;\n\t\t\tm_vertexBuffers[_handle.idx].create(m_commandBuffer, _size, NULL, layoutHandle, _flags);\n\t\t}\n\n\t\tvoid updateDynamicVertexBuffer(VertexBufferHandle _handle, uint32_t _offset, uint32_t _size, const Memory* _mem) override\n\t\t{\n\t\t\tm_vertexBuffers[_handle.idx].update(m_commandBuffer, _offset, bx::min<uint32_t>(_size, _mem->size), _mem->data);\n\t\t}\n\n\t\tvoid destroyDynamicVertexBuffer(VertexBufferHandle _handle) override\n\t\t{\n\t\t\tm_vertexBuffers[_handle.idx].destroy();\n\t\t}\n\n\t\tvoid createShader(ShaderHandle _handle, const Memory* _mem) override\n\t\t{\n\t\t\tm_shaders[_handle.idx].create(_mem);\n\t\t}\n\n\t\tvoid destroyShader(ShaderHandle _handle) override\n\t\t{\n\t\t\tm_shaders[_handle.idx].destroy();\n\t\t}\n\n\t\tvoid createProgram(ProgramHandle _handle, ShaderHandle _vsh, ShaderHandle _fsh) override\n\t\t{\n\t\t\tm_program[_handle.idx].create(&m_shaders[_vsh.idx], isValid(_fsh) ? &m_shaders[_fsh.idx] : NULL);\n\t\t}\n\n\t\tvoid destroyProgram(ProgramHandle _handle) override\n\t\t{\n\t\t\tm_program[_handle.idx].destroy();\n\t\t}\n\n\t\tvoid* createTexture(TextureHandle _handle, const Memory* _mem, uint64_t _flags, uint8_t _skip) override\n\t\t{\n\t\t\treturn m_textures[_handle.idx].create(m_commandBuffer, _mem, _flags, _skip);\n\t\t}\n\n\t\tvoid updateTextureBegin(TextureHandle /*_handle*/, uint8_t /*_side*/, uint8_t /*_mip*/) override\n\t\t{\n\t\t}\n\n\t\tvoid updateTexture(TextureHandle _handle, uint8_t _side, uint8_t _mip, const Rect& _rect, uint16_t _z, uint16_t _depth, uint16_t _pitch, const Memory* _mem) override\n\t\t{\n\t\t\tm_textures[_handle.idx].update(m_commandBuffer, _side, _mip, _rect, _z, _depth, _pitch, _mem);\n\t\t}\n\n\t\tvoid updateTextureEnd() override\n\t\t{\n\t\t}\n\n\t\tvoid readTexture(TextureHandle _handle, void* _data, uint8_t _mip) override\n\t\t{\n\t\t\tTextureVK& texture = m_textures[_handle.idx];\n\n\t\t\tuint32_t height = bx::uint32_max(1, texture.m_height >> _mip);\n\t\t\tuint32_t pitch  = texture.m_readback.pitch(_mip);\n\t\t\tuint32_t size = height * pitch;\n\n\t\t\tVkDeviceMemory stagingMemory;\n\t\t\tVkBuffer stagingBuffer;\n\t\t\tVK_CHECK(createReadbackBuffer(size, &stagingBuffer, &stagingMemory) );\n\n\t\t\ttexture.m_readback.copyImageToBuffer(\n\t\t\t\t  m_commandBuffer\n\t\t\t\t, stagingBuffer\n\t\t\t\t, texture.m_currentImageLayout\n\t\t\t\t, texture.m_aspectMask\n\t\t\t\t, _mip\n\t\t\t\t);\n\n\t\t\tkick(true);\n\n\t\t\ttexture.m_readback.readback(stagingMemory, 0, _data, _mip);\n\n\t\t\tvkDestroy(stagingBuffer);\n\t\t\tvkDestroy(stagingMemory);\n\t\t}\n\n\t\tvoid resizeTexture(TextureHandle _handle, uint16_t _width, uint16_t _height, uint8_t _numMips, uint16_t _numLayers) override\n\t\t{\n\t\t\tconst TextureVK& texture = m_textures[_handle.idx];\n\n\t\t\tconst TextureFormat::Enum format = TextureFormat::Enum(texture.m_requestedFormat);\n\t\t\tconst uint64_t flags = texture.m_flags;\n\n\t\t\tconst uint32_t size = sizeof(uint32_t) + sizeof(TextureCreate);\n\t\t\tconst Memory* mem = alloc(size);\n\n\t\t\tbx::StaticMemoryBlockWriter writer(mem->data, mem->size);\n\t\t\tuint32_t magic = BGFX_CHUNK_MAGIC_TEX;\n\t\t\tbx::write(&writer, magic, bx::ErrorAssert{});\n\n\t\t\tTextureCreate tc;\n\t\t\ttc.m_width     = _width;\n\t\t\ttc.m_height    = _height;\n\t\t\ttc.m_depth     = 0;\n\t\t\ttc.m_numLayers = _numLayers;\n\t\t\ttc.m_numMips   = _numMips;\n\t\t\ttc.m_format    = format;\n\t\t\ttc.m_cubeMap   = false;\n\t\t\ttc.m_mem       = NULL;\n\t\t\tbx::write(&writer, tc, bx::ErrorAssert{});\n\n\t\t\tdestroyTexture(_handle);\n\t\t\tcreateTexture(_handle, mem, flags, 0);\n\n\t\t\tbgfx::release(mem);\n\t\t}\n\n\t\tvoid overrideInternal(TextureHandle /*_handle*/, uintptr_t /*_ptr*/) override\n\t\t{\n\t\t}\n\n\t\tuintptr_t getInternal(TextureHandle /*_handle*/) override\n\t\t{\n\t\t\treturn 0;\n\t\t}\n\n\t\tvoid destroyTexture(TextureHandle _handle) override\n\t\t{\n\t\t\tm_imageViewCache.invalidateWithParent(_handle.idx);\n\t\t\tm_textures[_handle.idx].destroy();\n\t\t}\n\n\t\tvoid createFrameBuffer(FrameBufferHandle _handle, uint8_t _num, const Attachment* _attachment) override\n\t\t{\n\t\t\tm_frameBuffers[_handle.idx].create(_num, _attachment);\n\t\t}\n\n\t\tvoid createFrameBuffer(FrameBufferHandle _handle, void* _nwh, uint32_t _width, uint32_t _height, TextureFormat::Enum _format, TextureFormat::Enum _depthFormat) override\n\t\t{\n\t\t\tfor (uint32_t ii = 0, num = m_numWindows; ii < num; ++ii)\n\t\t\t{\n\t\t\t\tFrameBufferHandle handle = m_windows[ii];\n\t\t\t\tif (isValid(handle)\n\t\t\t\t&&  m_frameBuffers[handle.idx].m_nwh == _nwh)\n\t\t\t\t{\n\t\t\t\t\tdestroyFrameBuffer(handle);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tuint16_t denseIdx = m_numWindows++;\n\t\t\tm_windows[denseIdx] = _handle;\n\t\t\tVK_CHECK(m_frameBuffers[_handle.idx].create(denseIdx, _nwh, _width, _height, _format, _depthFormat) );\n\t\t}\n\n\t\tvoid destroyFrameBuffer(FrameBufferHandle _handle) override\n\t\t{\n\t\t\tFrameBufferVK& frameBuffer = m_frameBuffers[_handle.idx];\n\n\t\t\tif (_handle.idx == m_fbh.idx)\n\t\t\t{\n\t\t\t\tsetFrameBuffer(BGFX_INVALID_HANDLE, false);\n\t\t\t}\n\n\t\t\tuint16_t denseIdx = frameBuffer.destroy();\n\t\t\tif (UINT16_MAX != denseIdx)\n\t\t\t{\n\t\t\t\t--m_numWindows;\n\t\t\t\tif (m_numWindows > 1)\n\t\t\t\t{\n\t\t\t\t\tFrameBufferHandle handle = m_windows[m_numWindows];\n\t\t\t\t\tm_windows[m_numWindows]  = {kInvalidHandle};\n\t\t\t\t\tif (m_numWindows != denseIdx)\n\t\t\t\t\t{\n\t\t\t\t\t\tm_windows[denseIdx] = handle;\n\t\t\t\t\t\tm_frameBuffers[handle.idx].m_denseIdx = denseIdx;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvoid createUniform(UniformHandle _handle, UniformType::Enum _type, uint16_t _num, const char* _name) override\n\t\t{\n\t\t\tif (NULL != m_uniforms[_handle.idx])\n\t\t\t{\n\t\t\t\tBX_FREE(g_allocator, m_uniforms[_handle.idx]);\n\t\t\t}\n\n\t\t\tconst uint32_t size = bx::alignUp(g_uniformTypeSize[_type] * _num, 16);\n\t\t\tvoid* data = BX_ALLOC(g_allocator, size);\n\t\t\tbx::memSet(data, 0, size);\n\t\t\tm_uniforms[_handle.idx] = data;\n\t\t\tm_uniformReg.add(_handle, _name);\n\t\t}\n\n\t\tvoid destroyUniform(UniformHandle _handle) override\n\t\t{\n\t\t\tBX_FREE(g_allocator, m_uniforms[_handle.idx]);\n\t\t\tm_uniforms[_handle.idx] = NULL;\n\t\t}\n\n\t\tvoid requestScreenShot(FrameBufferHandle _fbh, const char* _filePath) override\n\t\t{\n\t\t\tconst FrameBufferVK& frameBuffer = isValid(_fbh)\n\t\t\t\t? m_frameBuffers[_fbh.idx]\n\t\t\t\t: m_backBuffer\n\t\t\t\t;\n\t\t\tconst SwapChainVK& swapChain = frameBuffer.m_swapChain;\n\n\t\t\tif (!isSwapChainReadable(swapChain) )\n\t\t\t{\n\t\t\t\tBX_TRACE("Unable to capture screenshot %s.", _filePath);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tauto callback = [](void* _src, uint32_t _width, uint32_t _height, uint32_t _pitch, const void* _userData)\n\t\t\t{\n\t\t\t\tconst char* filePath = (const char*)_userData;\n\t\t\t\tg_callback->screenShot(\n\t\t\t\t\t  filePath\n\t\t\t\t\t, _width\n\t\t\t\t\t, _height\n\t\t\t\t\t, _pitch\n\t\t\t\t\t, _src\n\t\t\t\t\t, _height * _pitch\n\t\t\t\t\t, false\n\t\t\t\t\t);\n\t\t\t};\n\n\t\t\tconst uint8_t bpp = bimg::getBitsPerPixel(bimg::TextureFormat::Enum(swapChain.m_colorFormat) );\n\t\t\tconst uint32_t size = frameBuffer.m_width * frameBuffer.m_height * bpp / 8;\n\n\t\t\tVkDeviceMemory stagingMemory;\n\t\t\tVkBuffer stagingBuffer;\n\t\t\tVK_CHECK(createReadbackBuffer(size, &stagingBuffer, &stagingMemory) );\n\n\t\t\treadSwapChain(swapChain, stagingBuffer, stagingMemory, callback, _filePath);\n\n\t\t\tvkDestroy(stagingBuffer);\n\t\t\tvkDestroy(stagingMemory);\n\t\t}\n\n\t\tvoid updateViewName(ViewId _id, const char* _name) override\n\t\t{\n\t\t\tbx::strCopy(&s_viewName[_id][BGFX_CONFIG_MAX_VIEW_NAME_RESERVED]\n\t\t\t\t, BX_COUNTOF(s_viewName[0]) - BGFX_CONFIG_MAX_VIEW_NAME_RESERVED\n\t\t\t\t, _name\n\t\t\t\t);\n\t\t}\n\n\t\tvoid updateUniform(uint16_t _loc, const void* _data, uint32_t _size) override\n\t\t{\n\t\t\tbx::memCopy(m_uniforms[_loc], _data, _size);\n\t\t}\n\n\t\tvoid invalidateOcclusionQuery(OcclusionQueryHandle _handle) override\n\t\t{\n\t\t\tm_occlusionQuery.invalidate(_handle);\n\t\t}\n\n\t\tvoid setMarker(const char* _marker, uint16_t _len) override\n\t\t{\n\t\t\tif (BX_ENABLED(BGFX_CONFIG_DEBUG_ANNOTATION) )\n\t\t\t{\n\t\t\t\tBX_UNUSED(_len);\n\n\t\t\t\tconst uint32_t abgr = kColorMarker;\n\n\t\t\t\tVkDebugUtilsLabelEXT dul;\n\t\t\t\tdul.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;\n\t\t\t\tdul.pNext = NULL;\n\t\t\t\tdul.pLabelName = _marker;\n\t\t\t\tdul.color[0] = ((abgr >> 24) & 0xff) / 255.0f;\n\t\t\t\tdul.color[1] = ((abgr >> 16) & 0xff) / 255.0f;\n\t\t\t\tdul.color[2] = ((abgr >> 8)  & 0xff) / 255.0f;\n\t\t\t\tdul.color[3] = ((abgr >> 0)  & 0xff) / 255.0f;\n\n\t\t\t\tvkCmdInsertDebugUtilsLabelEXT(m_commandBuffer, &dul);\n\t\t\t}\n\t\t}\n\n\t\tvirtual void setName(Handle _handle, const char* _name, uint16_t _len) override\n\t\t{\n\t\t\tswitch (_handle.type)\n\t\t\t{\n\t\t\tcase Handle::IndexBuffer:\n\t\t\t\tsetDebugObjectName(m_device, m_indexBuffers[_handle.idx].m_buffer, "%.*s", _len, _name);\n\t\t\t\tbreak;\n\n\t\t\tcase Handle::Shader:\n\t\t\t\tsetDebugObjectName(m_device, m_shaders[_handle.idx].m_module, "%.*s", _len, _name);\n\t\t\t\tbreak;\n\n\t\t\tcase Handle::Texture:\n\t\t\t\tsetDebugObjectName(m_device, m_textures[_handle.idx].m_textureImage, "%.*s", _len, _name);\n\n\t\t\t\tif (VK_NULL_HANDLE != m_textures[_handle.idx].m_singleMsaaImage)\n\t\t\t\t{\n\t\t\t\t\tsetDebugObjectName(m_device, m_textures[_handle.idx].m_singleMsaaImage, "%.*s", _len, _name);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase Handle::VertexBuffer:\n\t\t\t\tsetDebugObjectName(m_device, m_vertexBuffers[_handle.idx].m_buffer, "%.*s", _len, _name);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tBX_ASSERT(false, "Invalid handle type?! %d", _handle.type);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\ttemplate<typename Ty>\n\t\tvoid release(Ty& _object)\n\t\t{\n\t\t\tif (VK_NULL_HANDLE != _object)\n\t\t\t{\n\t\t\t\tm_cmd.release(uint64_t(_object.vk), getType<Ty>() );\n\t\t\t\t_object = VK_NULL_HANDLE;\n\t\t\t}\n\t\t}\n\n\t\tvoid submitBlit(BlitState& _bs, uint16_t _view);\n\n\t\tvoid submit(Frame* _render, ClearQuad& _clearQuad, TextVideoMemBlitter& _textVideoMemBlitter) override;\n\n\t\tvoid blitSetup(TextVideoMemBlitter& _blitter) override\n\t\t{\n\t\t\tconst uint32_t width  = m_backBuffer.m_width;\n\t\t\tconst uint32_t height = m_backBuffer.m_height;\n\n\t\t\tsetFrameBuffer(BGFX_INVALID_HANDLE);\n\n\t\t\tVkViewport vp;\n\t\t\tvp.x        = 0.0f;\n\t\t\tvp.y        =  float(height);\n\t\t\tvp.width    =  float(width);\n\t\t\tvp.height   = -float(height);\n\t\t\tvp.minDepth = 0.0f;\n\t\t\tvp.maxDepth = 1.0f;\n\t\t\tvkCmdSetViewport(m_commandBuffer, 0, 1, &vp);\n\n\t\t\tVkRect2D rc;\n\t\t\trc.offset.x      = 0;\n\t\t\trc.offset.y      = 0;\n\t\t\trc.extent.width  = width;\n\t\t\trc.extent.height = height;\n\t\t\tvkCmdSetScissor(m_commandBuffer, 0, 1, &rc);\n\n\t\t\tconst uint64_t state = 0\n\t\t\t\t| BGFX_STATE_WRITE_RGB\n\t\t\t\t| BGFX_STATE_WRITE_A\n\t\t\t\t| BGFX_STATE_DEPTH_TEST_ALWAYS\n\t\t\t\t| BGFX_STATE_MSAA\n\t\t\t\t;\n\n\t\t\tconst VertexLayout* layout = &m_vertexLayouts[_blitter.m_vb->layoutHandle.idx];\n\t\t\tVkPipeline pso = getPipeline(state\n\t\t\t\t, packStencil(BGFX_STENCIL_DEFAULT, BGFX_STENCIL_DEFAULT)\n\t\t\t\t, 1\n\t\t\t\t, &layout\n\t\t\t\t, _blitter.m_program\n\t\t\t\t, 0\n\t\t\t\t);\n\t\t\tvkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pso);\n\n\t\t\tProgramVK& program = m_program[_blitter.m_program.idx];\n\t\t\tfloat proj[16];\n\t\t\tbx::mtxOrtho(proj, 0.0f, (float)width, (float)height, 0.0f, 0.0f, 1000.0f, 0.0f, false);\n\n\t\t\tPredefinedUniform& predefined = m_program[_blitter.m_program.idx].m_predefined[0];\n\t\t\tuint8_t flags = predefined.m_type;\n\t\t\tsetShaderUniform(flags, predefined.m_loc, proj, 4);\n\n\t\t\tUniformBuffer* vcb = program.m_vsh->m_constantBuffer;\n\n\t\t\tif (NULL != vcb)\n\t\t\t{\n\t\t\t\tcommit(*vcb);\n\t\t\t}\n\n\t\t\tScratchBufferVK& scratchBuffer = m_scratchBuffer[m_cmd.m_currentFrameInFlight];\n\t\t\tconst uint32_t bufferOffset = scratchBuffer.write(m_vsScratch, program.m_vsh->m_size);\n\n\t\t\tconst TextureVK& texture = m_textures[_blitter.m_texture.idx];\n\n\t\t\tRenderBind bind;\n\t\t\tbind.clear();\n\t\t\tbind.m_bind[0].m_type = Binding::Texture;\n\t\t\tbind.m_bind[0].m_idx = _blitter.m_texture.idx;\n\t\t\tbind.m_bind[0].m_samplerFlags = (uint32_t)(texture.m_flags & BGFX_SAMPLER_BITS_MASK);\n\n\t\t\tconst VkDescriptorSet descriptorSet = getDescriptorSet(program, bind, scratchBuffer, NULL);\n\n\t\t\tvkCmdBindDescriptorSets(\n\t\t\t\t  m_commandBuffer\n\t\t\t\t, VK_PIPELINE_BIND_POINT_GRAPHICS\n\t\t\t\t, program.m_pipelineLayout\n\t\t\t\t, 0\n\t\t\t\t, 1\n\t\t\t\t, &descriptorSet\n\t\t\t\t, 1\n\t\t\t\t, &bufferOffset\n\t\t\t\t);\n\n\t\t\tconst VertexBufferVK& vb  = m_vertexBuffers[_blitter.m_vb->handle.idx];\n\t\t\tconst VkDeviceSize offset = 0;\n\t\t\tvkCmdBindVertexBuffers(m_commandBuffer, 0, 1, &vb.m_buffer, &offset);\n\n\t\t\tconst BufferVK& ib = m_indexBuffers[_blitter.m_ib->handle.idx];\n\t\t\tvkCmdBindIndexBuffer(\n\t\t\t\t  m_commandBuffer\n\t\t\t\t, ib.m_buffer\n\t\t\t\t, 0\n\t\t\t\t, VK_INDEX_TYPE_UINT16\n\t\t\t\t);\n\t\t}\n\n\t\tvoid blitRender(TextVideoMemBlitter& _blitter, uint32_t _numIndices) override\n\t\t{\n\t\t\tconst uint32_t numVertices = _numIndices*4/6;\n\t\t\tif (0 < numVertices && m_backBuffer.isRenderable() )\n\t\t\t{\n\t\t\t\tm_indexBuffers[_blitter.m_ib->handle.idx].update(m_commandBuffer, 0, _numIndices*2, _blitter.m_ib->data);\n\t\t\t\tm_vertexBuffers[_blitter.m_vb->handle.idx].update(m_commandBuffer, 0, numVertices*_blitter.m_layout.m_stride, _blitter.m_vb->data, true);\n\n\t\t\t\tVkRenderPassBeginInfo rpbi;\n\t\t\t\trpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;\n\t\t\t\trpbi.pNext = NULL;\n\t\t\t\trpbi.renderPass  = m_backBuffer.m_renderPass;\n\t\t\t\trpbi.framebuffer = m_backBuffer.m_currentFramebuffer;\n\t\t\t\trpbi.renderArea.offset.x = 0;\n\t\t\t\trpbi.renderArea.offset.y = 0;\n\t\t\t\trpbi.renderArea.extent.width  = m_backBuffer.m_width;\n\t\t\t\trpbi.renderArea.extent.height = m_backBuffer.m_height;\n\t\t\t\trpbi.clearValueCount = 0;\n\t\t\t\trpbi.pClearValues    = NULL;\n\n\t\t\t\tvkCmdBeginRenderPass(m_commandBuffer, &rpbi, VK_SUBPASS_CONTENTS_INLINE);\n\t\t\t\tvkCmdDrawIndexed(m_commandBuffer, _numIndices, 1, 0, 0, 0);\n\n\t\t\t\tvkCmdEndRenderPass(m_commandBuffer);\n\t\t\t}\n\t\t}\n\n\t\tvoid preReset()\n\t\t{\n\t\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_frameBuffers); ++ii)\n\t\t\t{\n\t\t\t\tm_frameBuffers[ii].preReset();\n\t\t\t}\n\n\t\t\tif (m_captureSize > 0)\n\t\t\t{\n\t\t\t\tg_callback->captureEnd();\n\n\t\t\t\trelease(m_captureBuffer);\n\t\t\t\trelease(m_captureMemory);\n\t\t\t\tm_captureSize = 0;\n\t\t\t}\n\t\t}\n\n\t\tvoid postReset()\n\t\t{\n\t\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_frameBuffers); ++ii)\n\t\t\t{\n\t\t\t\tm_frameBuffers[ii].postReset();\n\t\t\t}\n\n\t\t\tif (m_resolution.reset & BGFX_RESET_CAPTURE)\n\t\t\t{\n\t\t\t\tconst uint8_t bpp = bimg::getBitsPerPixel(bimg::TextureFormat::Enum(m_backBuffer.m_swapChain.m_colorFormat) );\n\t\t\t\tconst uint32_t captureSize = m_backBuffer.m_width * m_backBuffer.m_height * bpp / 8;\n\n\t\t\t\tconst uint8_t dstBpp = bimg::getBitsPerPixel(bimg::TextureFormat::BGRA8);\n\t\t\t\tconst uint32_t dstPitch = m_backBuffer.m_width * dstBpp / 8;\n\n\t\t\t\tif (captureSize > m_captureSize)\n\t\t\t\t{\n\t\t\t\t\trelease(m_captureBuffer);\n\t\t\t\t\trelease(m_captureMemory);\n\n\t\t\t\t\tm_captureSize = captureSize;\n\t\t\t\t\tVK_CHECK(createReadbackBuffer(m_captureSize, &m_captureBuffer, &m_captureMemory) );\n\t\t\t\t}\n\n\t\t\t\tg_callback->captureBegin(m_resolution.width, m_resolution.height, dstPitch, TextureFormat::BGRA8, false);\n\t\t\t}\n\t\t}\n\n\t\tbool updateResolution(const Resolution& _resolution)\n\t\t{\n\t\t\tconst bool suspended = !!(_resolution.reset & BGFX_RESET_SUSPEND);\n\n\t\t\tfloat maxAnisotropy = 1.0f;\n\t\t\tif (!!(_resolution.reset & BGFX_RESET_MAXANISOTROPY) )\n\t\t\t{\n\t\t\t\tmaxAnisotropy = m_deviceProperties.limits.maxSamplerAnisotropy;\n\t\t\t}\n\n\t\t\tif (m_maxAnisotropy != maxAnisotropy)\n\t\t\t{\n\t\t\t\tm_maxAnisotropy = maxAnisotropy;\n\t\t\t\tm_samplerCache.invalidate();\n\t\t\t\tm_samplerBorderColorCache.invalidate();\n\t\t\t}\n\n\t\t\tbool depthClamp = m_deviceFeatures.depthClamp && !!(_resolution.reset & BGFX_RESET_DEPTH_CLAMP);\n\n\t\t\tif (m_depthClamp != depthClamp)\n\t\t\t{\n\t\t\t\tm_depthClamp = depthClamp;\n\t\t\t\tm_pipelineStateCache.invalidate();\n\t\t\t}\n\n\t\t\tif (NULL == m_backBuffer.m_nwh)\n\t\t\t{\n\t\t\t\treturn suspended;\n\t\t\t}\n\n\t\t\tuint32_t flags = _resolution.reset & ~(0\n\t\t\t\t| BGFX_RESET_SUSPEND\n\t\t\t\t| BGFX_RESET_MAXANISOTROPY\n\t\t\t\t| BGFX_RESET_DEPTH_CLAMP\n\t\t\t\t);\n\n\t\t\t// Note: m_needToRefreshSwapchain is deliberately ignored when deciding whether to recreate the swapchain\n\t\t\t// because it can happen several frames before submit is called with the new resolution.\n\t\t\t// Instead, vkAcquireNextImageKHR and all draws to the backbuffer are skipped until the window size is updated.\n\t\t\t// That also fixes a related issue where VK_ERROR_OUT_OF_DATE_KHR is returned from\n\t\t\t// vkQueuePresentKHR when the window doesn\'t exist anymore, and vkGetPhysicalDeviceSurfaceCapabilitiesKHR\n\t\t\t// fails with VK_ERROR_SURFACE_LOST_KHR.\n\n\t\t\tif (false\n\t\t\t||  m_resolution.format != _resolution.format\n\t\t\t||  m_resolution.width  != _resolution.width\n\t\t\t||  m_resolution.height != _resolution.height\n\t\t\t||  m_resolution.reset  != flags\n\t\t\t||  m_backBuffer.m_swapChain.m_needToRecreateSurface)\n\t\t\t{\n\t\t\t\tflags &= ~BGFX_RESET_INTERNAL_FORCE;\n\n\t\t\t\tif (m_backBuffer.m_nwh != g_platformData.nwh)\n\t\t\t\t{\n\t\t\t\t\tm_backBuffer.m_nwh = g_platformData.nwh;\n\t\t\t\t}\n\n\t\t\t\tm_resolution = _resolution;\n\t\t\t\tm_resolution.reset = flags;\n\n\t\t\t\tm_textVideoMem.resize(false, _resolution.width, _resolution.height);\n\t\t\t\tm_textVideoMem.clear();\n\n\t\t\t\tpreReset();\n\n\t\t\t\tm_backBuffer.update(m_commandBuffer, m_resolution);\n\n\t\t\t\tpostReset();\n\t\t\t}\n\n\t\t\treturn suspended;\n\t\t}\n\n\t\tvoid setShaderUniform(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs)\n\t\t{\n\t\t\tif (_flags & kUniformFragmentBit)\n\t\t\t{\n\t\t\t\tbx::memCopy(&m_fsScratch[_regIndex], _val, _numRegs*16);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbx::memCopy(&m_vsScratch[_regIndex], _val, _numRegs*16);\n\t\t\t}\n\t\t}\n\n\t\tvoid setShaderUniform4f(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs)\n\t\t{\n\t\t\tsetShaderUniform(_flags, _regIndex, _val, _numRegs);\n\t\t}\n\n\t\tvoid setShaderUniform4x4f(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs)\n\t\t{\n\t\t\tsetShaderUniform(_flags, _regIndex, _val, _numRegs);\n\t\t}\n\n\t\tvoid setFrameBuffer(FrameBufferHandle _fbh, bool _acquire = true)\n\t\t{\n\t\t\tBX_ASSERT(false\n\t\t\t\t  ||  isValid(_fbh)\n\t\t\t\t  ||  NULL != m_backBuffer.m_nwh\n\t\t\t\t, "Rendering to backbuffer in headless mode."\n\t\t\twds[2].pBufferInfo = NULL;\n\t\t\twds[2].pTexelBufferView = NULL;\n\n\t\t\tm_vsChanges = 0;\n\t\t\tm_fsChanges = 0;\n\n\t\t\tvkUpdateDescriptorSets(m_device, 3, wds, 0, NULL);\n\t\t\tvkCmdBindDescriptorSets(\n\t\t\t\tm_commandBuffer\n\n\t\t\tFrameBufferVK& oldFrameBuffer = isValid(m_fbh)\n\t\t\t\t? m_frameBuffers[m_fbh.idx]\n\t\t\t\t: m_backBuffer\n\t\t\t\t;\n\n\t\t\tif (NULL == oldFrameBuffer.m_nwh\n\t\t\t&&  m_fbh.idx != _fbh.idx)\n\t\t\t{\n\t\t\t\toldFrameBuffer.resolve();\n\n\t\t\t\tfor (uint8_t ii = 0, num = oldFrameBuffer.m_num; ii < num; ++ii)\n\t\t\t\t{\n\t\t\t\t\tTextureVK& texture = m_textures[oldFrameBuffer.m_texture[ii].idx];\n\t\t\t\t\ttexture.setImageMemoryBarrier(m_commandBuffer, texture.m_sampledLayout);\n\t\t\t\t\tif (VK_NULL_HANDLE != texture.m_singleMsaaImage)\n\t\t\t\t\t{\n\t\t\t\t\t\ttexture.setImageMemoryBarrier(m_commandBuffer, texture.m_sampledLayout, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (isValid(oldFrameBuffer.m_depth) )\n\t\t\t\t{\n\t\t\t\t\tTextureVK& texture = m_textures[oldFrameBuffer.m_depth.idx];\n\t\t\t\t\tconst bool writeOnly  = 0 != (texture.m_flags&BGFX_TEXTURE_RT_WRITE_ONLY);\n\n\t\t\t\t\tif (!writeOnly)\n\t\t\t\t\t{\n\t\t\t\t\t\ttexture.setImageMemoryBarrier(m_commandBuffer, texture.m_sampledLayout);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (NULL == newFrameBuffer.m_nwh)\n\t\t\t{\n\t\t\t\tfor (uint8_t ii = 0, num = newFrameBuffer.m_num; ii < num; ++ii)\n\t\t\t\t{\n\t\t\t\t\tTextureVK& texture = m_textures[newFrameBuffer.m_texture[ii].idx];\n\t\t\t\t\ttexture.setImageMemoryBarrier(\n\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL\n\t\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (isValid(newFrameBuffer.m_depth) )\n\t\t\t\t{\n\t\t\t\t\tTextureVK& texture = m_textures[newFrameBuffer.m_depth.idx];\n\t\t\t\t\ttexture.setImageMemoryBarrier(\n\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL\n\t\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tnewFrameBuffer.acquire(m_commandBuffer);\n\t\t\t}\n\n\t\t\tif (_acquire)\n\t\t\t{\n\t\t\t\tint64_t start = bx::getHPCounter();\n\n\t\t\t\tnewFrameBuffer.acquire(m_commandBuffer);\n\n\t\t\t\tint64_t now = bx::getHPCounter();\n\n\t\t\t\tif (NULL != newFrameBuffer.m_nwh)\n\t\t\t\t{\n\t\t\t\t\tm_presentElapsed += now - start;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tm_fbh = _fbh;\n\t\t}\n\n\t\tvoid setDebugWireframe(bool _wireframe)\n\t\t{\n\t\t\tconst bool wireframe = m_deviceFeatures.fillModeNonSolid && _wireframe;\n\t\t\tif (m_wireframe != wireframe)\n\t\t\t{\n\t\t\t\tm_wireframe = wireframe;\n\t\t\t\tm_pipelineStateCache.invalidate();\n\t\t\t}\n\t\t}\n\n\t\tvoid setBlendState(VkPipelineColorBlendStateCreateInfo& _desc, uint64_t _state, uint32_t _rgba = 0)\n\t\t{\n\t\t\tVkPipelineColorBlendAttachmentState* bas = const_cast<VkPipelineColorBlendAttachmentState*>(_desc.pAttachments);\n\n\t\t\tuint8_t writeMask = 0;\n\t\t\twriteMask |= (_state & BGFX_STATE_WRITE_R) ? VK_COLOR_COMPONENT_R_BIT : 0;\n\t\t\twriteMask |= (_state & BGFX_STATE_WRITE_G) ? VK_COLOR_COMPONENT_G_BIT : 0;\n\t\t\twriteMask |= (_state & BGFX_STATE_WRITE_B) ? VK_COLOR_COMPONENT_B_BIT : 0;\n\t\t\twriteMask |= (_state & BGFX_STATE_WRITE_A) ? VK_COLOR_COMPONENT_A_BIT : 0;\n\n\t\t\tbas->blendEnable = !!(BGFX_STATE_BLEND_MASK & _state);\n\n\t\t\t{\n\t\t\t\tconst uint32_t blend    = uint32_t( (_state & BGFX_STATE_BLEND_MASK         ) >> BGFX_STATE_BLEND_SHIFT);\n\t\t\t\tconst uint32_t equation = uint32_t( (_state & BGFX_STATE_BLEND_EQUATION_MASK) >> BGFX_STATE_BLEND_EQUATION_SHIFT);\n\n\t\t\t\tconst uint32_t srcRGB = (blend      ) & 0xf;\n\t\t\t\tconst uint32_t dstRGB = (blend >>  4) & 0xf;\n\t\t\t\tconst uint32_t srcA   = (blend >>  8) & 0xf;\n\t\t\t\tconst uint32_t dstA   = (blend >> 12) & 0xf;\n\n\t\t\t\tconst uint32_t equRGB = (equation     ) & 0x7;\n\t\t\t\tconst uint32_t equA   = (equation >> 3) & 0x7;\n\n\t\t\t\tbas->srcColorBlendFactor = s_blendFactor[srcRGB][0];\n\t\t\t\tbas->dstColorBlendFactor = s_blendFactor[dstRGB][0];\n\t\t\t\tbas->colorBlendOp        = s_blendEquation[equRGB];\n\n\t\t\t\tbas->srcAlphaBlendFactor = s_blendFactor[srcA][1];\n\t\t\t\tbas->dstAlphaBlendFactor = s_blendFactor[dstA][1];\n\t\t\t\tbas->alphaBlendOp        = s_blendEquation[equA];\n\n\t\t\t\tbas->colorWriteMask = writeMask;\n\t\t\t}\n\n\t\t\tconst FrameBufferVK& frameBuffer = isValid(m_fbh)\n\t\t\t\t? m_frameBuffers[m_fbh.idx]\n\t\t\t\t: m_backBuffer\n\t\t\t\t;\n\n\t\t\tconst uint32_t numAttachments = NULL == frameBuffer.m_nwh\n\t\t\t\t? frameBuffer.m_num\n\t\t\t\t: 1\n\t\t\t\t;\n\n\t\t\tif (!!(BGFX_STATE_BLEND_INDEPENDENT & _state)\n\t\t\t&&  m_deviceFeatures.independentBlend )\n\t\t\t{\n\t\t\t\tfor (uint32_t ii = 1, rgba = _rgba; ii < numAttachments; ++ii, rgba >>= 11)\n\t\t\t\t{\n\t\t\t\t\t++bas;\n\t\t\t\t\tbas->blendEnable =  0 != (rgba & 0x7ff);\n\n\t\t\t\t\tconst uint32_t src      = (rgba     ) & 0xf;\n\t\t\t\t\tconst uint32_t dst      = (rgba >> 4) & 0xf;\n\t\t\t\t\tconst uint32_t equation = (rgba >> 8) & 0x7;\n\n\t\t\t\t\tbas->srcColorBlendFactor = s_blendFactor[src][0];\n\t\t\t\t\tbas->dstColorBlendFactor = s_blendFactor[dst][0];\n\t\t\t\t\tbas->colorBlendOp        = s_blendEquation[equation];\n\n\t\t\t\t\tbas->srcAlphaBlendFactor = s_blendFactor[src][1];\n\t\t\t\t\tbas->dstAlphaBlendFactor = s_blendFactor[dst][1];\n\t\t\t\t\tbas->alphaBlendOp        = s_blendEquation[equation];\n\n\t\t\t\t\tbas->colorWriteMask = writeMask;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfor (uint32_t ii = 1; ii < numAttachments; ++ii)\n\t\t\t\t{\n\t\t\t\t\tbx::memCopy(&bas[ii], bas, sizeof(VkPipelineColorBlendAttachmentState) );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;\n\t\t\t_desc.pNext = NULL;\n\t\t\t_desc.flags = 0;\n\t\t\t_desc.logicOpEnable = VK_FALSE;\n\t\t\t_desc.logicOp       = VK_LOGIC_OP_CLEAR;\n\t\t\t_desc.attachmentCount = numAttachments;\n\t\t\t_desc.blendConstants[0] = 0.0f;\n\t\t\t_desc.blendConstants[1] = 0.0f;\n\t\t\t_desc.blendConstants[2] = 0.0f;\n\t\t\t_desc.blendConstants[3] = 0.0f;\n\t\t}\n\n\t\tvoid setRasterizerState(VkPipelineRasterizationStateCreateInfo& _desc, uint64_t _state, bool _wireframe = false)\n\t\t{\n\t\t\tconst uint32_t cull = (_state&BGFX_STATE_CULL_MASK) >> BGFX_STATE_CULL_SHIFT;\n\n\t\t\t_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;\n\t\t\t_desc.pNext = NULL;\n\t\t\t_desc.flags = 0;\n\t\t\t_desc.depthClampEnable = m_deviceFeatures.depthClamp && m_depthClamp;\n\t\t\t_desc.rasterizerDiscardEnable = VK_FALSE;\n\t\t\t_desc.polygonMode = m_deviceFeatures.fillModeNonSolid && _wireframe\n\t\t\t\t? VK_POLYGON_MODE_LINE\n\t\t\t\t: VK_POLYGON_MODE_FILL\n\t\t\t\t;\n\t\t\t_desc.cullMode  = s_cullMode[cull];\n\t\t\t_desc.frontFace = (_state&BGFX_STATE_FRONT_CCW) ? VK_FRONT_FACE_COUNTER_CLOCKWISE : VK_FRONT_FACE_CLOCKWISE;\n\t\t\t_desc.depthBiasEnable = VK_FALSE;\n\t\t\t_desc.depthBiasConstantFactor = 0.0f;\n\t\t\t_desc.depthBiasClamp          = 0.0f;\n\t\t\t_desc.depthBiasSlopeFactor    = 0.0f;\n\t\t\t_desc.lineWidth               = 1.0f;\n\t\t}\n\n\t\tvoid setConservativeRasterizerState(VkPipelineRasterizationConservativeStateCreateInfoEXT& _desc, uint64_t _state)\n\t\t{\n\t\t\t_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT;\n\t\t\t_desc.pNext = NULL;\n\t\t\t_desc.flags = 0;\n\t\t\t_desc.conservativeRasterizationMode = (_state&BGFX_STATE_CONSERVATIVE_RASTER)\n\t\t\t\t? VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT\n\n\t\tvoid setShaderUniform(uint8_t _flags, uint32_t _regIndex, const void* _val, uint32_t _numRegs)\n\t\t{\n\t\t\tBX_UNUSED(_flags, _regIndex, _val, _numRegs);\n\t\t\tif (_flags&BGFX_UNIFORM_FRAGMENTBIT)\n\t\t\t{\n\t\t\t\tbx::memCopy(&m_fsScratch[_regIndex], _val, _numRegs*16);\n\t\t\t\tm_fsChanges += _numRegs;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbx::memCopy(&m_vsScratch[_regIndex], _val, _numRegs*16);\n\t\t\t\tm_vsChanges += _numRegs;\n\t\t\t}\n\t\t}\n\n\t\t\t_desc.stippledLineEnable = VK_FALSE;\n\t\t\t_desc.lineStippleFactor = 0;\n\t\t\t_desc.lineStipplePattern = 0;\n\t\t}\n\n\t\tvoid setDepthStencilState(VkPipelineDepthStencilStateCreateInfo& _desc, uint64_t _state, uint64_t _stencil = 0)\n\t\t{\n\t\t\tconst uint32_t fstencil = unpackStencil(0, _stencil);\n\t\t\tuint32_t func = (_state&BGFX_STATE_DEPTH_TEST_MASK)>>BGFX_STATE_DEPTH_TEST_SHIFT;\n\n\t\t\t_desc.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;\n\t\t\t_desc.pNext = NULL;\n\t\t\t_desc.flags = 0;\n\t\t\t_desc.depthTestEnable  = 0 != func;\n\t\t\t_desc.depthWriteEnable = !!(BGFX_STATE_WRITE_Z & _state);\n\t\t\t_desc.depthCompareOp   = s_cmpFunc[func];\n\t\t\t_desc.depthBoundsTestEnable = VK_FALSE;\n\n\t\t\t_desc.stencilTestEnable = 0 != _stencil;\n\n\t\t\tuint32_t bstencil = unpackStencil(1, _stencil);\n\t\t\tuint32_t frontAndBack = bstencil != BGFX_STENCIL_NONE && bstencil != fstencil;\n\t\t\tbstencil = frontAndBack ? bstencil : fstencil;\n\n\t\t\t_desc.front.failOp      = s_stencilOp[(fstencil & BGFX_STENCIL_OP_FAIL_S_MASK) >> BGFX_STENCIL_OP_FAIL_S_SHIFT];\n\t\t\t_desc.front.passOp      = s_stencilOp[(fstencil & BGFX_STENCIL_OP_PASS_Z_MASK) >> BGFX_STENCIL_OP_PASS_Z_SHIFT];\n\t\t\t_desc.front.depthFailOp = s_stencilOp[(fstencil & BGFX_STENCIL_OP_FAIL_Z_MASK) >> BGFX_STENCIL_OP_FAIL_Z_SHIFT];\n\t\t\t_desc.front.compareOp   = s_cmpFunc[(fstencil & BGFX_STENCIL_TEST_MASK) >> BGFX_STENCIL_TEST_SHIFT];\n\t\t\t_desc.front.compareMask = UINT32_MAX;\n\t\t\t_desc.front.writeMask   = UINT32_MAX;\n\t\t\t_desc.front.reference   = 0;\n\n\t\t\t_desc.back.failOp       = s_stencilOp[(bstencil & BGFX_STENCIL_OP_FAIL_S_MASK) >> BGFX_STENCIL_OP_FAIL_S_SHIFT];\n\t\t\t_desc.back.passOp       = s_stencilOp[(bstencil & BGFX_STENCIL_OP_PASS_Z_MASK) >> BGFX_STENCIL_OP_PASS_Z_SHIFT];\n\t\t\t_desc.back.depthFailOp  = s_stencilOp[(bstencil & BGFX_STENCIL_OP_FAIL_Z_MASK) >> BGFX_STENCIL_OP_FAIL_Z_SHIFT];\n\t\t\t_desc.back.compareOp    = s_cmpFunc[(bstencil&BGFX_STENCIL_TEST_MASK) >> BGFX_STENCIL_TEST_SHIFT];\n\t\t\t_desc.back.compareMask  = UINT32_MAX;\n\t\t\t_desc.back.writeMask    = UINT32_MAX;\n\t\t\t_desc.back.reference    = 0;\n\n\t\t\t_desc.minDepthBounds = 0.0f;\n//\t\t\t\t\t, NULL\n//\t\t\t\t\t);\n//\t\t\t}\n//\n//\t\t\tm_vsChanges = 0;\n//\t\t\tm_fsChanges = 0;\n//\t\t}\n\n\t\tvoid setFrameBuffer(FrameBufferHandle _fbh, bool _msaa = true)\n\t\t\t_vertexInputState.pNext = NULL;\n\t\t\t_vertexInputState.flags = 0;\n\n\t\t\t_vertexInputState.vertexBindingDescriptionCount   = 0;\n\t\t\t_vertexInputState.vertexAttributeDescriptionCount = 0;\n\n\t\t\tuint16_t unsettedAttr[Attrib::Count];\n\t\t\tbx::memCopy(unsettedAttr, _program.m_vsh->m_attrMask, sizeof(uint16_t) * Attrib::Count);\n\n\t\t\tfor (uint8_t stream = 0; stream < _numStream; ++stream)\n\t\t\t{\n\t\t\t\tVertexLayout layout;\n\t\t\t\tbx::memCopy(&layout, _layout[stream], sizeof(VertexLayout) );\n\t\t\t\tconst uint16_t* attrMask = _program.m_vsh->m_attrMask;\n\n\t\t\t\tfor (uint32_t ii = 0; ii < Attrib::Count; ++ii)\n\t\t\t\t{\n\t\t\t\t\tuint16_t mask = attrMask[ii];\n\t\t\t\t\tuint16_t attr = (layout.m_attributes[ii] & mask);\n\t\t\t\t\tlayout.m_attributes[ii] = attr == 0 || attr == UINT16_MAX ? UINT16_MAX : attr;\n\n\t\t\t\t\tif (unsettedAttr[ii] && attr != UINT16_MAX)\n\t\t\t\t\t{\n\t\t\t\t\t\tunsettedAttr[ii] = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfillVertexLayout(_program.m_vsh, _vertexInputState, layout);\n\t\t\t}\n\n\t\t\tfor (uint32_t ii = 0; ii < Attrib::Count; ++ii)\n\t\t\t{\n\t\t\t\tif (0 < unsettedAttr[ii])\n\t\t\t\t{\n\t\t\t\t\tuint32_t numAttribs  = _vertexInputState.vertexAttributeDescriptionCount;\n\t\t\t\t\tVkVertexInputAttributeDescription* inputAttrib = const_cast<VkVertexInputAttributeDescription*>(_vertexInputState.pVertexAttributeDescriptions + numAttribs);\n\t\t\t\t\tinputAttrib->location = _program.m_vsh->m_attrRemap[ii];\n\t\t\t\t\tinputAttrib->binding  = 0;\n\t\t\t\t\tinputAttrib->format = VK_FORMAT_R32G32B32_SFLOAT;\n\t\t\t\t\tinputAttrib->offset = 0;\n\t\t\t\t\t_vertexInputState.vertexAttributeDescriptionCount++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (0 < _numInstanceData)\n\t\t\t{\n\t\t\t\tfillInstanceBinding(_program.m_vsh, _vertexInputState, _numInstanceData);\n\t\t\t}\n\t\t}\n\n\t\tVkResult getRenderPass(uint8_t _num, const VkFormat* _formats, const VkImageAspectFlags* _aspects, const bool* _resolve, VkSampleCountFlagBits _samples, ::VkRenderPass* _renderPass)\n\t\t{\n\t\t\tVkResult result = VK_SUCCESS;\n\n\t\t\tif (VK_SAMPLE_COUNT_1_BIT == _samples)\n\t\t\t{\n\t\t\t\t_resolve = NULL;\n\t\t\t}\n\n\t\t\tbx::HashMurmur2A hash;\n\t\t\thash.begin();\n\t\t\thash.add(_samples);\n\t\t\thash.add(_formats, sizeof(VkFormat) * _num);\n\t\t\tif (NULL != _resolve)\n\t\t\t{\n\t\t\t\thash.add(_resolve, sizeof(bool) * _num);\n\t\t\t}\n\t\t\tuint32_t hashKey = hash.end();\n\n\t\t\tVkRenderPass renderPass = m_renderPassCache.find(hashKey);\n\n\t\t\tif (VK_NULL_HANDLE != renderPass)\n\t\t\t{\n\t\t\t\t*_renderPass = renderPass;\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tVkAttachmentDescription ad[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS * 2];\n\n\t\t\tfor (uint8_t ii = 0; ii < (_num * 2); ++ii)\n\t\t\t{\n\t\t\t\tad[ii].flags          = 0;\n\t\t\t\tad[ii].format         = VK_FORMAT_UNDEFINED;\n\t\t\t\tad[ii].samples        = _samples;\n\t\t\t\tad[ii].loadOp         = VK_ATTACHMENT_LOAD_OP_LOAD;\n\t\t\t\tad[ii].storeOp        = VK_ATTACHMENT_STORE_OP_STORE;\n\t\t\t\tad[ii].stencilLoadOp  = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\t\t\t\tad[ii].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n\t\t\t\tad[ii].initialLayout  = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t\t\t\tad[ii].finalLayout    = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t\t\t}\n\n\t\t\tVkAttachmentReference colorAr[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];\n\t\t\tVkAttachmentReference resolveAr[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];\n\t\t\tVkAttachmentReference depthAr;\n\t\t\tuint32_t numColorAr = 0;\n\t\t\tuint32_t numResolveAr = 0;\n\n\t\t\tcolorAr[0].attachment   = VK_ATTACHMENT_UNUSED;\n\t\t\tcolorAr[0].layout       = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t\t\tresolveAr[0].attachment = VK_ATTACHMENT_UNUSED;\n\t\t\tresolveAr[0].layout     = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t\t\tdepthAr.attachment      = VK_ATTACHMENT_UNUSED;\n\t\t\tdepthAr.layout          = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n\n\t\t\tfor (uint8_t ii = 0; ii < _num; ++ii)\n\t\t\t{\n\t\t\t\tad[ii].format = _formats[ii];\n\n\t\t\t\tif (_aspects[ii] & VK_IMAGE_ASPECT_COLOR_BIT)\n\t\t\t\t{\n\t\t\t\t\tcolorAr[numColorAr].layout     = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t\t\t\t\tcolorAr[numColorAr].attachment = ii;\n\n\t\t\t\t\tresolveAr[numColorAr].layout     = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\t\t\t\t\tresolveAr[numColorAr].attachment = VK_ATTACHMENT_UNUSED;\n\t\t\t\t\tif (NULL != _resolve\n\t\t\t\t\t&&  _resolve[ii])\n\t\t\t\t\t{\n\t\t\t\t\t\tconst uint32_t resolve = _num + numResolveAr;\n\n\t\t\t\t\t\tad[resolve].format  = _formats[ii];\n\t\t\t\t\t\tad[resolve].samples = VK_SAMPLE_COUNT_1_BIT;\n\t\t\t\t\t\tad[resolve].loadOp  = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n\n\t\t\t\t\t\tresolveAr[numColorAr].attachment = resolve;\n\t\t\t\t\t\tnumResolveAr++;\n\t\t\t\t\t}\n\n\t\t\t\t\tnumColorAr++;\n\t\t\t\t}\n\t\t\t\telse if (_aspects[ii] & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) )\n\t\t\t\t{\n\t\t\t\t\tad[ii].stencilLoadOp  = VK_ATTACHMENT_LOAD_OP_LOAD;\n\t\t\t\t\tad[ii].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;\n\t\t\t\t\tad[ii].initialLayout  = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n\t\t\t\t\tad[ii].finalLayout    = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n\n\t\t\t\t\tdepthAr.layout     = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n\t\t\t\t\tdepthAr.attachment = ii;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tVkSubpassDescription sd[1];\n\t\t\tsd[0].flags                   = 0;\n\t\t\tsd[0].pipelineBindPoint       = VK_PIPELINE_BIND_POINT_GRAPHICS;\n\t\t\tsd[0].inputAttachmentCount    = 0;\n\t\t\tsd[0].pInputAttachments       = NULL;\n\t\t\tsd[0].colorAttachmentCount    = bx::max<uint32_t>(numColorAr, 1);\n\t\t\tsd[0].pColorAttachments       = colorAr;\n\t\t\tsd[0].pResolveAttachments     = resolveAr;\n\t\t\tsd[0].pDepthStencilAttachment = &depthAr;\n\t\t\tsd[0].preserveAttachmentCount = 0;\n\t\t\tsd[0].pPreserveAttachments    = NULL;\n\n\t\t\tconst VkPipelineStageFlags graphicsStages = 0\n\t\t\t\t| VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT\n\t\t\t\t| VK_PIPELINE_STAGE_VERTEX_INPUT_BIT\n\t\t\t\t| VK_PIPELINE_STAGE_VERTEX_SHADER_BIT\n\t\t\t\t| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT\n\t\t\t\t| VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT\n\t\t\t\t| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT\n\t\t\t\t| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT\n\t\t\t\t;\n\t\t\tconst VkPipelineStageFlags outsideStages = 0\n\t\t\t\t| graphicsStages\n\t\t\t\t| VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT\n\t\t\t\t| VK_PIPELINE_STAGE_TRANSFER_BIT\n\t\t\t\t;\n\n\t\t\tVkSubpassDependency dep[2];\n\n\t\t\tdep[0].srcSubpass      = VK_SUBPASS_EXTERNAL;\n\t\t\tdep[0].dstSubpass      = 0;\n\t\t\tdep[0].srcStageMask    = outsideStages;\n\t\t\tdep[0].dstStageMask    = graphicsStages;\n\t\t\tdep[0].srcAccessMask   = VK_ACCESS_MEMORY_WRITE_BIT;\n\t\t\tdep[0].dstAccessMask   = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;\n\t\t\tdep[0].dependencyFlags = 0;\n\n\t\t\tdep[1].srcSubpass      = BX_COUNTOF(sd)-1;\n\t\t\tdep[1].dstSubpass      = VK_SUBPASS_EXTERNAL;\n\t\t\tdep[1].srcStageMask    = graphicsStages;\n\t\t\tdep[1].dstStageMask    = outsideStages;\n\t\t\tdep[1].srcAccessMask   = VK_ACCESS_MEMORY_WRITE_BIT;\n\t\t\tdep[1].dstAccessMask   = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;\n\t\t\tdep[1].dependencyFlags = 0;\n\n\t\t\tVkRenderPassCreateInfo rpi;\n\t\t\trpi.sType           = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;\n\t\t\trpi.pNext           = NULL;\n\t\t\trpi.flags           = 0;\n\t\t\trpi.attachmentCount = _num + numResolveAr;\n\t\t\trpi.pAttachments    = ad;\n\t\t\trpi.subpassCount    = BX_COUNTOF(sd);\n\t\t\trpi.pSubpasses      = sd;\n\t\t\trpi.dependencyCount = BX_COUNTOF(dep);\n\t\t\trpi.pDependencies   = dep;\n\n\t\t\tresult = vkCreateRenderPass(m_device, &rpi, m_allocatorCb, &renderPass);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create render pass error: vkCreateRenderPass failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tm_renderPassCache.add(hashKey, renderPass);\n\n\t\t\t*_renderPass = renderPass;\n\n\t\t\treturn result;\n\t\t}\n\n\t\tVkResult getRenderPass(uint8_t _num, const Attachment* _attachments, ::VkRenderPass* _renderPass)\n\t\t{\n\t\t\tVkFormat formats[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];\n\t\t\tVkImageAspectFlags aspects[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];\n\t\t\tVkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;\n\n\t\t\tfor (uint8_t ii = 0; ii < _num; ++ii)\n\t\t\t{\n\t\t\t\tconst TextureVK& texture = m_textures[_attachments[ii].handle.idx];\n\t\t\t\tformats[ii] = texture.m_format;\n\t\t\t\taspects[ii] = texture.m_aspectMask;\n\t\t\t\tsamples = texture.m_sampler.Sample;\n\t\t\t}\n\n\t\t\treturn getRenderPass(_num, formats, aspects, NULL, samples, _renderPass);\n\t\t}\n\n\t\tVkResult getRenderPass(const SwapChainVK& swapChain, ::VkRenderPass* _renderPass)\n\t\t{\n\t\t\tconst VkFormat formats[2] =\n\t\t\t{\n\t\t\t\tswapChain.m_sci.imageFormat,\n\t\t\t\tswapChain.m_backBufferDepthStencil.m_format\n\t\t\t};\n\t\t\tconst VkImageAspectFlags aspects[2] =\n\t\t\t{\n\t\t\t\tVK_IMAGE_ASPECT_COLOR_BIT,\n\t\t\t\tswapChain.m_backBufferDepthStencil.m_aspectMask\n\t\t\t};\n\t\t\tconst bool resolve[2] =\n\t\t\t{\n\t\t\t\tswapChain.m_supportsManualResolve ? false : true,\n\t\t\t\tfalse\n\t\t\t};\n\t\t\tconst VkSampleCountFlagBits samples = swapChain.m_sampler.Sample;\n\n\t\t\treturn getRenderPass(BX_COUNTOF(formats), formats, aspects, resolve, samples, _renderPass);\n\t\t}\n\n\t\tVkSampler getSampler(uint32_t _flags, VkFormat _format, const float _palette[][4])\n\t\t{\n\t\t\tuint32_t index = ((_flags & BGFX_SAMPLER_BORDER_COLOR_MASK) >> BGFX_SAMPLER_BORDER_COLOR_SHIFT);\n\t\t\tindex = bx::min<uint32_t>(BGFX_CONFIG_MAX_COLOR_PALETTE - 1, index);\n\n\t\t\t_flags &= BGFX_SAMPLER_BITS_MASK;\n\t\t\t_flags &= ~(m_deviceFeatures.samplerAnisotropy ? 0 : (BGFX_SAMPLER_MIN_ANISOTROPIC | BGFX_SAMPLER_MAG_ANISOTROPIC) );\n\n\t\t\t// Force both min+max anisotropic, can\'t be set individually.\n\t\t\t_flags |= 0 != (_flags & (BGFX_SAMPLER_MIN_ANISOTROPIC|BGFX_SAMPLER_MAG_ANISOTROPIC) )\n\t\t\t\t\t? BGFX_SAMPLER_MIN_ANISOTROPIC|BGFX_SAMPLER_MAG_ANISOTROPIC\n\t\t\t\t\t: 0\n\t\t\t\t\t;\n\n\t\t\tconst float* rgba = NULL == _palette\n\t\t\t\t? NULL\n\t\t\t\t: _palette[index]\n\t\t\t\t;\n\n\t\t\tconst bool needColor = true\n\t\t\t\t&& needBorderColor(_flags)\n\t\t\t\t&& NULL != rgba\n\t\t\t\t&& m_borderColorSupport\n\t\t\t\t;\n\n\t\t\tuint32_t hashKey;\n\t\t\tVkSampler sampler = VK_NULL_HANDLE;\n\t\t\tif (!needColor)\n\t\t\t{\n\t\t\t\tbx::HashMurmur2A hash;\n\t\t\t\thash.begin();\n\t\t\t\thash.add(_flags);\n\t\t\t\thash.add(-1);\n\t\t\t\thash.add(VK_FORMAT_UNDEFINED);\n\t\t\t\thashKey = hash.end();\n\n\t\t\t\tsampler = m_samplerCache.find(hashKey);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbx::HashMurmur2A hash;\n\t\t\t\thash.begin();\n\t\t\t\thash.add(_flags);\n\t\t\t\thash.add(index);\n\t\t\t\thash.add(_format);\n\t\t\t\thashKey = hash.end();\n\n\t\t\t\tconst uint32_t colorHashKey = m_samplerBorderColorCache.find(hashKey);\n\t\t\t\tconst uint32_t newColorHashKey = bx::hash<bx::HashMurmur2A>(rgba, sizeof(float) * 4);\n\t\t\t\tif (newColorHashKey == colorHashKey)\n\t\t\t\t{\n\t\t\t\t\tsampler = m_samplerCache.find(hashKey);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tm_samplerBorderColorCache.add(hashKey, newColorHashKey);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (VK_NULL_HANDLE != sampler)\n\t\t\t{\n\t\t\t\treturn sampler;\n\t\t\t}\n\n\t\t\tconst uint32_t cmpFunc = (_flags&BGFX_SAMPLER_COMPARE_MASK)>>BGFX_SAMPLER_COMPARE_SHIFT;\n\n\t\t\tconst float maxLodBias = m_deviceProperties.limits.maxSamplerLodBias;\n\t\t\tconst float lodBias = bx::clamp(float(BGFX_CONFIG_MIP_LOD_BIAS), -maxLodBias, maxLodBias);\n\n\t\t\tVkSamplerCreateInfo sci;\n\t\t\tsci.sType            = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;\n\t\t\tsci.pNext            = NULL;\n\t\t\tsci.flags            = 0;\n\t\t\tsci.magFilter        = _flags & BGFX_SAMPLER_MAG_POINT ? VK_FILTER_NEAREST              : VK_FILTER_LINEAR;\n\t\t\tsci.minFilter        = _flags & BGFX_SAMPLER_MIN_POINT ? VK_FILTER_NEAREST              : VK_FILTER_LINEAR;\n\t\t\tsci.mipmapMode       = _flags & BGFX_SAMPLER_MIP_POINT ? VK_SAMPLER_MIPMAP_MODE_NEAREST : VK_SAMPLER_MIPMAP_MODE_LINEAR;\n\t\t\tsci.addressModeU     = s_textureAddress[(_flags&BGFX_SAMPLER_U_MASK)>>BGFX_SAMPLER_U_SHIFT];\n\t\t\tsci.addressModeV     = s_textureAddress[(_flags&BGFX_SAMPLER_V_MASK)>>BGFX_SAMPLER_V_SHIFT];\n\t\t\tsci.addressModeW     = s_textureAddress[(_flags&BGFX_SAMPLER_W_MASK)>>BGFX_SAMPLER_W_SHIFT];\n\t\t\tsci.mipLodBias       = lodBias;\n\t\t\tsci.anisotropyEnable = !!(_flags & (BGFX_SAMPLER_MIN_ANISOTROPIC | BGFX_SAMPLER_MAG_ANISOTROPIC) );\n\t\t\tsci.maxAnisotropy    = m_maxAnisotropy;\n\t\t\tsci.compareEnable    = 0 != cmpFunc;\n\t\t\tsci.compareOp        = s_cmpFunc[cmpFunc];\n\t\t\tsci.minLod           = 0.0f;\n\t\t\tsci.maxLod           = VK_LOD_CLAMP_NONE;\n\t\t\tsci.borderColor      = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;\n\t\t\tsci.unnormalizedCoordinates = VK_FALSE;\n\n\t\t\tVkSamplerCustomBorderColorCreateInfoEXT cbcci;\n\t\t\tif (needColor)\n\t\t\t{\n\t\t\t\tcbcci.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT;\n\t\t\t\tcbcci.pNext = NULL;\n\t\t\t\tcbcci.format = _format;\n\t\t\t\tbx::memCopy(cbcci.customBorderColor.float32, rgba, sizeof(cbcci.customBorderColor.float32) );\n\n\t\t\t\tsci.pNext = &cbcci;\n\t\t\t\tsci.borderColor = VK_BORDER_COLOR_FLOAT_CUSTOM_EXT;\n\t\t\t}\n\n\t\t\tVK_CHECK(vkCreateSampler(m_device, &sci, m_allocatorCb, &sampler) );\n\n\t\t\tm_samplerCache.add(hashKey, sampler);\n\t\t\treturn sampler;\n\t\t}\n\n\t\tVkImageView getCachedImageView(TextureHandle _handle, uint32_t _mip, uint32_t _numMips, VkImageViewType _type, bool _stencil = false)\n\t\t{\n\t\t\tconst TextureVK& texture = m_textures[_handle.idx];\n\n\t\t\t_stencil = _stencil && !!(texture.m_aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT);\n\n\t\t\tbx::HashMurmur2A hash;\n\t\t\thash.begin();\n\t\t\thash.add(_handle.idx);\n\t\t\thash.add(_mip);\n\t\t\thash.add(_numMips);\n\t\t\thash.add(_type);\n\t\t\thash.add(_stencil);\n\t\t\tuint32_t hashKey = hash.end();\n\n\t\t\tVkImageView* viewCached = m_imageViewCache.find(hashKey);\n\n\t\t\tif (NULL != viewCached)\n\t\t\t{\n\t\t\t\treturn *viewCached;\n\t\t\t}\n\n\t\t\tconst VkImageAspectFlags aspectMask = 0\n\t\t\t\t| VK_IMAGE_ASPECT_COLOR_BIT\n\t\t\t\t| ( _stencil ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT)\n\t\t\t\t;\n\n\t\t\tVkImageView view;\n\t\t\tVK_CHECK(texture.createView(0, texture.m_numSides, _mip, _numMips, _type, aspectMask, false, &view) );\n\t\t\tm_imageViewCache.add(hashKey, view, _handle.idx);\n\n\t\t\treturn view;\n\t\t}\n\n\t\tVkPipeline getPipeline(ProgramHandle _program)\n\t\t{\n\t\t\tProgramVK& program = m_program[_program.idx];\n\n\t\t\tbx::HashMurmur2A murmur;\n\t\t\tmurmur.begin();\n\t\t\tmurmur.add(program.m_vsh->m_hash);\n\t\t\tconst uint32_t hash = murmur.end();\n\n\t\t\tVkPipeline pipeline = m_pipelineStateCache.find(hash);\n\n\t\t\tif (VK_NULL_HANDLE != pipeline)\n\t\t\t{\n\t\t\t\treturn pipeline;\n\t\t\t}\n\n\t\t\tVkComputePipelineCreateInfo cpci;\n\t\t\tcpci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;\n\t\t\tcpci.pNext = NULL;\n\t\t\tcpci.flags = 0;\n\n\t\t\tcpci.stage.sType  = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n\t\t\tcpci.stage.pNext  = NULL;\n\t\t\tcpci.stage.flags  = 0;\n\t\t\tcpci.stage.stage  = VK_SHADER_STAGE_COMPUTE_BIT;\n\t\t\tcpci.stage.module = program.m_vsh->m_module;\n\t\t\tcpci.stage.pName  = "main";\n\t\t\tcpci.stage.pSpecializationInfo = NULL;\n\n\t\t\tcpci.layout             = program.m_pipelineLayout;\n\t\t\tcpci.basePipelineHandle = VK_NULL_HANDLE;\n\t\t\tcpci.basePipelineIndex  = 0;\n\n\t\t\tVK_CHECK(vkCreateComputePipelines(m_device, m_pipelineCache, 1, &cpci, m_allocatorCb, &pipeline) );\n\n\t\t\tm_pipelineStateCache.add(hash, pipeline);\n\n\t\t\treturn pipeline;\n\t\t}\n\n\t\tVkPipeline getPipeline(uint64_t _state, uint64_t _stencil, uint8_t _numStreams, const VertexLayout** _layouts, ProgramHandle _program, uint8_t _numInstanceData)\n\t\t{\n\t\t\tProgramVK& program = m_program[_program.idx];\n\n\t\t\t_state &= 0\n\t\t\t\t| BGFX_STATE_WRITE_MASK\n\t\t\t\t| BGFX_STATE_DEPTH_TEST_MASK\n\t\t\t\t| BGFX_STATE_BLEND_MASK\n\t\t\t\t| BGFX_STATE_BLEND_EQUATION_MASK\n\t\t\t\t| (g_caps.supported & BGFX_CAPS_BLEND_INDEPENDENT ? BGFX_STATE_BLEND_INDEPENDENT : 0)\n\t\t\t\t| BGFX_STATE_BLEND_ALPHA_TO_COVERAGE\n\t\t\t\t| BGFX_STATE_CULL_MASK\n\t\t\t\t| BGFX_STATE_FRONT_CCW\n\t\t\t\t| BGFX_STATE_MSAA\n\t\t\t\t| (m_lineAASupport ? BGFX_STATE_LINEAA : 0)\n\t\t\t\t| (g_caps.supported & BGFX_CAPS_CONSERVATIVE_RASTER ? BGFX_STATE_CONSERVATIVE_RASTER : 0)\n\t\t\t\t| BGFX_STATE_PT_MASK\n\t\t\t\t;\n\n\t\t\t_stencil &= packStencil(~BGFX_STENCIL_FUNC_REF_MASK, ~BGFX_STENCIL_FUNC_REF_MASK);\n\n\t\t\tVertexLayout layout;\n\t\t\tif (0 < _numStreams)\n\t\t\t{\n\t\t\t\tbx::memCopy(&layout, _layouts[0], sizeof(VertexLayout) );\n\t\t\t\tconst uint16_t* attrMask = program.m_vsh->m_attrMask;\n\n\t\t\t\tfor (uint32_t ii = 0; ii < Attrib::Count; ++ii)\n\t\t\t\t{\n\t\t\t\t\tuint16_t mask = attrMask[ii];\n\t\t\t\t\tuint16_t attr = (layout.m_attributes[ii] & mask);\n\t\t\t\t\tlayout.m_attributes[ii] = attr == 0 ? UINT16_MAX : attr == UINT16_MAX ? 0 : attr;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst FrameBufferVK& frameBuffer = isValid(m_fbh)\n\t\t\t\t? m_frameBuffers[m_fbh.idx]\n\t\t\t\t: m_backBuffer\n\t\t\t\t;\n\n\t\t\tbx::HashMurmur2A murmur;\n\t\t\tmurmur.begin();\n\t\t\tmurmur.add(_state);\n\t\t\tmurmur.add(_stencil);\n\t\t\tmurmur.add(program.m_vsh->m_hash);\n\t\t\tmurmur.add(program.m_vsh->m_attrMask, sizeof(program.m_vsh->m_attrMask) );\n\n\t\t\tif (NULL != program.m_fsh)\n\t\t\t{\n\t\t\t\tmurmur.add(program.m_fsh->m_hash);\n\t\t\t}\n\n\t\t\tfor (uint8_t ii = 0; ii < _numStreams; ++ii)\n\t\t\t{\n\t\t\t\tmurmur.add(_layouts[ii]->m_hash);\n\t\t\t}\n\n\t\t\tmurmur.add(layout.m_attributes, sizeof(layout.m_attributes) );\n\t\t\tmurmur.add(_numInstanceData);\n\t\t\tmurmur.add(frameBuffer.m_renderPass);\n\t\t\tconst uint32_t hash = murmur.end();\n\n\t\t\tVkPipeline pipeline = m_pipelineStateCache.find(hash);\n\n\t\t\tif (VK_NULL_HANDLE != pipeline)\n\t\t\t{\n\t\t\t\treturn pipeline;\n\t\t\t}\n\n\t\t\tVkPipelineColorBlendAttachmentState blendAttachmentState[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];\n\t\t\tVkPipelineColorBlendStateCreateInfo colorBlendState;\n\t\t\tcolorBlendState.pAttachments = blendAttachmentState;\n\t\t\tsetBlendState(colorBlendState, _state);\n\n\t\t\tVkPipelineInputAssemblyStateCreateInfo inputAssemblyState;\n\t\t\tinputAssemblyState.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;\n\t\t\tinputAssemblyState.pNext = NULL;\n\t\t\tinputAssemblyState.flags = 0;\n\t\t\tinputAssemblyState.topology = s_primInfo[(_state&BGFX_STATE_PT_MASK) >> BGFX_STATE_PT_SHIFT].m_topology;\n\t\t\tinputAssemblyState.primitiveRestartEnable = VK_FALSE;\n\n\t\t\tVkPipelineRasterizationStateCreateInfo rasterizationState;\n\t\t\tsetRasterizerState(rasterizationState, _state, m_wireframe);\n\n\t\t\tVkBaseInStructure* nextRasterizationState = (VkBaseInStructure*)&rasterizationState;\n\n\t\t\tVkPipelineRasterizationConservativeStateCreateInfoEXT conservativeRasterizationState;\n\t\t\tif (s_extension[Extension::EXT_conservative_rasterization].m_supported)\n\t\t\t{\n\t\t\t\tnextRasterizationState->pNext = (VkBaseInStructure*)&conservativeRasterizationState;\n\t\t\t\tnextRasterizationState = (VkBaseInStructure*)&conservativeRasterizationState;\n\t\t\tmurmur.add(_stencil);\n\t\t\tmurmur.add(program.m_vsh->m_hash);\n\t\t\tmurmur.add(program.m_vsh->m_attrMask, sizeof(program.m_vsh->m_attrMask) );\n\t\t\tmurmur.add(program.m_fsh->m_hash);\n\t\t\tfor (uint8_t ii = 0; ii < _numStreams; ++ii)\n\t\t\t{\n\t\t\t\tmurmur.add(_layouts[ii]->m_hash);\n\n\t\t\tVkPipelineDepthStencilStateCreateInfo depthStencilState;\n\t\t\tsetDepthStencilState(depthStencilState, _state, _stencil);\n\n\t\t\tVkVertexInputBindingDescription  inputBinding[BGFX_CONFIG_MAX_VERTEX_STREAMS + 1];\n\t\t\tVkVertexInputAttributeDescription inputAttrib[Attrib::Count + BGFX_CONFIG_MAX_INSTANCE_DATA_COUNT];\n\n\t\t\tVkPipelineVertexInputStateCreateInfo vertexInputState;\n\t\t\tvertexInputState.pVertexBindingDescriptions   = inputBinding;\n\t\t\tvertexInputState.pVertexAttributeDescriptions = inputAttrib;\n\t\t\tsetInputLayout(vertexInputState, _numStreams, _layouts, program, _numInstanceData);\n\n\t\t\tconst VkDynamicState dynamicStates[] =\n\t\t\t{\n\t\t\t\tVK_DYNAMIC_STATE_VIEWPORT,\n\t\t\t\tVK_DYNAMIC_STATE_SCISSOR,\n\t\t\t\tVK_DYNAMIC_STATE_BLEND_CONSTANTS,\n\t\t\t\tVK_DYNAMIC_STATE_STENCIL_REFERENCE,\n\t\t\t};\n\n\t\t\tVkPipelineDynamicStateCreateInfo dynamicState;\n\t\t\tdynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;\n\t\t\tdynamicState.pNext = NULL;\n\t\t\tdynamicState.flags = 0;\n\t\t\tdynamicState.dynamicStateCount = BX_COUNTOF(dynamicStates);\n\t\t\tdynamicState.pDynamicStates    = dynamicStates;\n\n\t\t\tVkPipelineShaderStageCreateInfo shaderStages[2];\n\t\t\tshaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n\t\t\tshaderStages[0].pNext = NULL;\n\t\t\tshaderStages[0].flags = 0;\n\t\t\tshaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;\n\t\t\tshaderStages[0].module = program.m_vsh->m_module;\n\t\t\tshaderStages[0].pName  = "main";\n\t\t\tshaderStages[0].pSpecializationInfo = NULL;\n\n\t\t\tif (NULL != program.m_fsh)\n\t\t\t{\n\t\t\t\tshaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n\t\t\t\tshaderStages[1].pNext = NULL;\n\t\t\t\tshaderStages[1].flags = 0;\n\t\t\t\tshaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;\n\t\t\t\tshaderStages[1].module = program.m_fsh->m_module;\n\t\t\t\tshaderStages[1].pName  = "main";\n\t\t\t\tshaderStages[1].pSpecializationInfo = NULL;\n\t\t\t}\n\n\t\t\tVkPipelineViewportStateCreateInfo viewportState;\n\t\t\tviewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;\n\t\t\tviewportState.pNext = NULL;\n\t\t\tviewportState.flags = 0;\n\t\t\tviewportState.viewportCount = 1;\n\t\t\tviewportState.pViewports    = NULL;\n\t\t\tviewportState.scissorCount  = 1;\n\t\t\tviewportState.pScissors     = NULL;\n\n\t\t\tVkPipelineMultisampleStateCreateInfo multisampleState;\n\t\t\tmultisampleState.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;\n\t\t\tmultisampleState.pNext = NULL;\n\t\t\tshaderStages[0].module = program.m_vsh->m_module;\n\t\t\tshaderStages[0].pName  = "main";\n\t\t\tshaderStages[0].pSpecializationInfo = NULL;\n\t\t\tshaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n\t\t\tshaderStages[1].pNext = NULL;\n\t\t\tshaderStages[1].flags = 0;\n\t\t\tshaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;\n\t\t\tshaderStages[1].module = program.m_fsh->m_module;\n\t\t\tshaderStages[1].pName  = "main";\n\t\t\tshaderStages[1].pSpecializationInfo = NULL;\n\n\t\t\tVkPipelineViewportStateCreateInfo viewportState;\n\t\t\tviewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;\n\t\t\tgraphicsPipeline.pViewportState      = &viewportState;\n\t\t\tgraphicsPipeline.pRasterizationState = &rasterizationState;\n\t\t\tgraphicsPipeline.pMultisampleState   = &multisampleState;\n\t\t\tgraphicsPipeline.pDepthStencilState  = &depthStencilState;\n\t\t\tgraphicsPipeline.pColorBlendState    = &colorBlendState;\n\t\t\tgraphicsPipeline.pDynamicState       = &dynamicState;\n\t\t\tgraphicsPipeline.layout     = program.m_pipelineLayout;\n\t\t\tgraphicsPipeline.renderPass = frameBuffer.m_renderPass;\n\t\t\tgraphicsPipeline.subpass    = 0;\n\t\t\tgraphicsPipeline.basePipelineHandle = VK_NULL_HANDLE;\n\t\t\tgraphicsPipeline.basePipelineIndex  = 0;\n\n\t\t\tuint32_t length = g_callback->cacheReadSize(hash);\n\t\t\tbool cached = length > 0;\n\n\t\t\tvoid* cachedData = NULL;\n\n\t\t\tVkPipelineCacheCreateInfo pcci;\n\t\t\tpcci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;\n\t\t\tgraphicsPipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;\n\t\t\tgraphicsPipeline.pNext = NULL;\n\t\t\tgraphicsPipeline.flags = 0;\n\t\t\tgraphicsPipeline.stageCount = BX_COUNTOF(shaderStages);\n\t\t\tgraphicsPipeline.pStages    = shaderStages;\n\t\t\tgraphicsPipeline.pVertexInputState   = &vertexInputState;\n\t\t\tgraphicsPipeline.pInputAssemblyState = &inputAssemblyState;\n\t\t\t\tcachedData = BX_ALLOC(g_allocator, length);\n\t\t\t\tif (g_callback->cacheRead(hash, cachedData, length) )\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Loading cached pipeline state (size %d).", length);\n\t\t\t\t\tbx::MemoryReader reader(cachedData, length);\n\n\t\t\t\t\tpcci.initialDataSize = (size_t)reader.remaining();\n\t\t\t\t\tpcci.pInitialData    = reader.getDataPtr();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tVkPipelineCache cache;\n\t\t\tVK_CHECK(vkCreatePipelineCache(m_device, &pcci, m_allocatorCb, &cache) );\n\n\t\t\tVK_CHECK(vkCreateGraphicsPipelines(\n\t\t\t\t  m_device\n\t\t\t\t, cache\n\t\t\t\t, 1\n\t\t\t\t, &graphicsPipeline\n\t\t\t\t, m_allocatorCb\n\t\t\t\t, &pipeline\n\t\t\t\t) );\n\t\t\tm_pipelineStateCache.add(hash, pipeline);\n\n\t\t\tsize_t dataSize;\n\t\t\tVK_CHECK(vkGetPipelineCacheData(m_device, cache, &dataSize, NULL) );\n\n\t\t\tif (0 < dataSize)\n\t\t\t{\n\t\t\t\tif (length < dataSize)\n\t\t\t\t{\n\t\t\t\t\tcachedData = BX_REALLOC(g_allocator, cachedData, dataSize);\n\t\t\t\t}\n\n\t\t\t\tVK_CHECK(vkGetPipelineCacheData(m_device, cache, &dataSize, cachedData) );\n\t\t\t\tg_callback->cacheWrite(hash, cachedData, (uint32_t)dataSize);\n\t\t\t}\n\n\t\t\tVK_CHECK(vkMergePipelineCaches(m_device, m_pipelineCache, 1, &cache) );\n\t\t\tvkDestroy(cache);\n\n\t\t\tif (NULL != cachedData)\n\t\t\t{\n\t\t\t\tBX_FREE(g_allocator, cachedData);\n\t\t\t}\n\n\t\t\treturn pipeline;\n\t\t}\n\n\t\tVkDescriptorSet getDescriptorSet(const ProgramVK& program, const RenderBind& renderBind, const ScratchBufferVK& scratchBuffer, const float _palette[][4])\n\t\t{\n\t\t\tVkDescriptorSet descriptorSet;\n\n\t\t\tVkDescriptorSetAllocateInfo dsai;\n\t\t\tdsai.sType              = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;\n\t\t\tdsai.pNext              = NULL;\n\t\t\tdsai.descriptorPool     = m_descriptorPool;\n\t\t\tdsai.descriptorSetCount = 1;\n\t\t\tdsai.pSetLayouts        = &program.m_descriptorSetLayout;\n\n\t\t\tVK_CHECK(vkAllocateDescriptorSets(m_device, &dsai, &descriptorSet) );\n\n\t\t\tVkDescriptorImageInfo  imageInfo[BGFX_CONFIG_MAX_TEXTURE_SAMPLERS];\n\t\t\tVkDescriptorBufferInfo bufferInfo[BGFX_CONFIG_MAX_TEXTURE_SAMPLERS];\n\n\t\t\tconstexpr uint32_t kMaxDescriptorSets = 2 * BGFX_CONFIG_MAX_TEXTURE_SAMPLERS + 2;\n\t\t\tVkWriteDescriptorSet wds[kMaxDescriptorSets] = {};\n\n\t\t\tuint32_t wdsCount    = 0;\n\t\t\tuint32_t bufferCount = 0;\n\t\t\tuint32_t imageCount  = 0;\n\n\t\t\tfor (uint32_t stage = 0; stage < BGFX_CONFIG_MAX_TEXTURE_SAMPLERS; ++stage)\n\t\t\t{\n\t\t\t\tconst Binding& bind = renderBind.m_bind[stage];\n\t\t\t\tconst BindInfo& bindInfo = program.m_bindInfo[stage];\n\n\t\t\t\tif (kInvalidHandle != bind.m_idx\n\t\t\t\t&&  isValid(bindInfo.uniformHandle) )\n\t\t\t\t{\n\t\t\t\t\tswitch (bind.m_type)\n\t\t\t\t\t{\n\t\t\t\t\tcase Binding::Image:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst bool isImageDescriptor = BindType::Image == bindInfo.type;\n\n\t\t\t\t\t\t\twds[wdsCount].sType            = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n\t\t\t\t\t\t\twds[wdsCount].pNext            = NULL;\n\t\t\t\t\t\t\twds[wdsCount].dstSet           = descriptorSet;\n\t\t\t\t\t\t\twds[wdsCount].dstBinding       = bindInfo.binding;\n\t\t\t\t\t\t\twds[wdsCount].dstArrayElement  = 0;\n\t\t\t\t\t\t\twds[wdsCount].descriptorCount  = 1;\n\t\t\t\t\t\t\twds[wdsCount].descriptorType   = isImageDescriptor\n\t\t\t\t\t\t\t\t? VK_DESCRIPTOR_TYPE_STORAGE_IMAGE\n\t\t\t\t\t\t\t\t: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE\n\t\t\t\t\t\t\t\t;\n\t\t\t\t\t\t\twds[wdsCount].pImageInfo       = NULL;\n\t\t\t\t\t\t\twds[wdsCount].pBufferInfo      = NULL;\n\t\t\t\t\t\t\twds[wdsCount].pTexelBufferView = NULL;\n\n\t\t\t\t\t\t\tconst TextureVK& texture = m_textures[bind.m_idx];\n\n\t\t\t\t\t\t\tVkImageViewType type = texture.m_type;\n\t\t\t\t\t\t\tif (UINT32_MAX != bindInfo.index)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype = program.m_textures[bindInfo.index].type;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse if (type == VK_IMAGE_VIEW_TYPE_CUBE\n\t\t\t\t\t\t\t     ||  type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tBX_ASSERT(\n\t\t\t\t\t\t\t\t  texture.m_currentImageLayout == texture.m_sampledLayout\n\t\t\t\t\t\t\t\t, "Mismatching image layout. Texture currently used as a framebuffer attachment?"\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\timageInfo[imageCount].imageLayout = texture.m_sampledLayout;\n\t\t\t\t\t\t\timageInfo[imageCount].sampler     = VK_NULL_HANDLE;\n\t\t\t\t\t\t\timageInfo[imageCount].imageView   = getCachedImageView(\n\t\t\t\t\t\t\t\t  { bind.m_idx }\n\t\t\t\t\t\t\t\t, bind.m_mip\n\t\t\t\t\t\t\t\t, 1\n\t\t\t\t\t\t\t\t, type\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\twds[wdsCount].pImageInfo = &imageInfo[imageCount];\n\t\t\t\t\t\t\t++imageCount;\n\n\t\t\t\t\t\t\t++wdsCount;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Binding::VertexBuffer:\n\t\t\t\t\tcase Binding::IndexBuffer:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\twds[wdsCount].sType            = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n\t\t\t\t\t\t\twds[wdsCount].pNext            = NULL;\n\t\t\t\t\t\t\twds[wdsCount].dstSet           = descriptorSet;\n\t\t\t\t\t\t\twds[wdsCount].dstBinding       = bindInfo.binding;\n\t\t\t\t\t\t\twds[wdsCount].dstArrayElement  = 0;\n\t\t\t\t\t\t\twds[wdsCount].descriptorCount  = 1;\n\t\t\t\t\t\t\twds[wdsCount].descriptorType   = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;\n\t\t\t\t\t\t\twds[wdsCount].pImageInfo       = NULL;\n\t\t\t\t\t\t\twds[wdsCount].pBufferInfo      = NULL;\n\t\t\t\t\t\t\twds[wdsCount].pTexelBufferView = NULL;\n\n\t\t\t\t\t\t\tconst BufferVK& sb = bind.m_type == Binding::VertexBuffer\n\t\t\t\t\t\t\t\t? m_vertexBuffers[bind.m_idx]\n\t\t\t\t\t\t\t\t: m_indexBuffers[bind.m_idx]\n\t\t\t\t\t\t\t\t;\n\n\t\t\t\t\t\t\tbufferInfo[bufferCount].buffer = sb.m_buffer;\n\t\t\t\t\t\t\tbufferInfo[bufferCount].offset = 0;\n\t\t\t\t\t\t\tbufferInfo[bufferCount].range  = sb.m_size;\n\t\t\t\t\t\t\twds[wdsCount].pBufferInfo = &bufferInfo[bufferCount];\n\t\t\t\t\t\t\t++bufferCount;\n\n\t\t\t\t\t\t\t++wdsCount;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase Binding::Texture:\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tTextureVK& texture = m_textures[bind.m_idx];\n\t\t\t\t\t\t\tconst uint32_t samplerFlags = 0 == (BGFX_SAMPLER_INTERNAL_DEFAULT & bind.m_samplerFlags)\n\t\t\t\t\t\t\t\t? bind.m_samplerFlags\n\t\t\t\t\t\t\t\t: (uint32_t)texture.m_flags\n\t\t\t\t\t\t\t\t;\n\t\t\t\t\t\t\tconst bool sampleStencil = !!(samplerFlags & BGFX_SAMPLER_SAMPLE_STENCIL);\n\t\t\t\t\t\t\tVkSampler sampler = getSampler(samplerFlags, texture.m_format, _palette);\n\n\t\t\t\t\t\t\tconst VkImageViewType type = UINT32_MAX == bindInfo.index\n\t\t\t\t\t\t\t\t? texture.m_type\n\t\t\t\t\t\t\t\t: program.m_textures[bindInfo.index].type\n\t\t\t\t\t\t\t\t;\n\n\t\t\t\t\t\t\tBX_ASSERT(\n\t\t\t\t\t\t\t\t  texture.m_currentImageLayout == texture.m_sampledLayout\n\t\t\t\t\t\t\t\t, "Mismatching image layout. Texture currently used as a framebuffer attachment?"\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\timageInfo[imageCount].imageLayout = texture.m_sampledLayout;\n\t\t\t\t\t\t\timageInfo[imageCount].sampler     = sampler;\n\t\t\t\t\t\t\timageInfo[imageCount].imageView   = getCachedImageView(\n\t\t\t\t\t\t\t\t  { bind.m_idx }\n\t\t\t\t\t\t\t\t, 0\n\t\t\t\t\t\t\t\t, texture.m_numMips\n\t\t\t\t\t\t\t\t, type\n\t\t\t\t\t\t\t\t, sampleStencil\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\twds[wdsCount].sType            = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n\t\t\t\t\t\t\twds[wdsCount].pNext            = NULL;\n\t\t\t\t\t\t\twds[wdsCount].dstSet           = descriptorSet;\n\t\t\t\t\t\t\twds[wdsCount].dstBinding       = bindInfo.binding;\n\t\t\t\t\t\t\twds[wdsCount].dstArrayElement  = 0;\n\t\t\t\t\t\t\twds[wdsCount].descriptorCount  = 1;\n\t\t\t\t\t\t\twds[wdsCount].descriptorType   = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n\t\t\t\t\t\t\twds[wdsCount].pImageInfo       = &imageInfo[imageCount];\n\t\t\t\t\t\t\twds[wdsCount].pBufferInfo      = NULL;\n\t\t\t\t\t\t\twds[wdsCount].pTexelBufferView = NULL;\n\t\t\t\t\t\t\t++wdsCount;\n\n\t\t\t\t\t\t\twds[wdsCount].sType            = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n\t\t\t\t\t\t\twds[wdsCount].pNext            = NULL;\n\t\t\t\t\t\t\twds[wdsCount].dstSet           = descriptorSet;\n\t\t\t\t\t\t\twds[wdsCount].dstBinding       = bindInfo.samplerBinding;\n\t\t\t\t\t\t\twds[wdsCount].dstArrayElement  = 0;\n\t\t\t\t\t\t\twds[wdsCount].descriptorCount  = 1;\n\t\t\t\t\t\t\twds[wdsCount].descriptorType   = VK_DESCRIPTOR_TYPE_SAMPLER;\n\t\t\t\t\t\t\twds[wdsCount].pImageInfo       = &imageInfo[imageCount];\n\t\t\t\t\t\t\twds[wdsCount].pBufferInfo      = NULL;\n\t\t\t\t\t\t\twds[wdsCount].pTexelBufferView = NULL;\n\t\t\t\t\t\t\t++wdsCount;\n\n\t\t\t\t\t\t\t++imageCount;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst uint32_t vsize = program.m_vsh->m_size;\n\t\t\tconst uint32_t fsize = NULL != program.m_fsh ? program.m_fsh->m_size : 0;\n\n\t\t\tif (0 < total)\n\t\t\t{\n\t\t\t\tuint32_t vsUniformBinding = program.m_vsh->m_uniformBinding;\n\t\t\t\tuint32_t fsUniformBinding = program.m_fsh ? program.m_fsh->m_uniformBinding : 0;\n\n\t\t\t\tif (vsize > 0)\n\t\t\t\t{\n\t\t\t\twds[wdsCount].pNext            = NULL;\n\t\t\t\twds[wdsCount].dstSet           = descriptorSet;\n\t\t\t\twds[wdsCount].dstBinding       = program.m_vsh->m_uniformBinding;\n\t\t\t\twds[wdsCount].dstArrayElement  = 0;\n\t\t\t\twds[wdsCount].descriptorCount  = 1;\n\t\t\t\twds[wdsCount].descriptorType   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;\n\t\t\t\twds[wdsCount].pImageInfo       = NULL;\n\t\t\t\twds[wdsCount].pBufferInfo      = &bufferInfo[bufferCount];\n\t\t\t\twds[wdsCount].pTexelBufferView = NULL;\n\t\t\t\t++wdsCount;\n\t\t\t\t++bufferCount;\n\t\t\t}\n\n\t\t\tif (fsize > 0)\n\t\t\t{\n\t\t\t\tbufferInfo[bufferCount].buffer = scratchBuffer.m_buffer;\n\t\t\t\tbufferInfo[bufferCount].offset = 0;\n\t\t\t\tbufferInfo[bufferCount].range  = fsize;\n\n\t\t\t\twds[wdsCount].sType            = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n\t\t\t\twds[wdsCount].pNext            = NULL;\n\t\t\t\twds[wdsCount].dstSet           = descriptorSet;\n\t\t\t\twds[wdsCount].dstBinding       = program.m_fsh->m_uniformBinding;\n\t\t\t\twds[wdsCount].dstArrayElement  = 0;\n\t\t\t\twds[wdsCount].descriptorCount  = 1;\n\t\t\t\twds[wdsCount].descriptorType   = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;\n\t\t\t\twds[wdsCount].pImageInfo       = NULL;\n\t\t\t\twds[wdsCount].pBufferInfo      = &bufferInfo[bufferCount];\n\t\t\t\twds[wdsCount].pTexelBufferView = NULL;\n\t\t\t\t++wdsCount;\n\t\t\t\t++bufferCount;\n\t\t\t}\n\n\t\t\tvkUpdateDescriptorSets(m_device, wdsCount, wds, 0, NULL);\n\n\t\t\tVkDescriptorSet temp = descriptorSet;\n\t\t\trelease(temp);\n\n\t\t\treturn descriptorSet;\n\t\t}\n\n\t\tbool isSwapChainReadable(const SwapChainVK& _swapChain)\n\t\t{\n\t\t\treturn true\n\t\t\t\t&& NULL != _swapChain.m_nwh\n\t\t\t\t&& _swapChain.m_needPresent\n\t\t\t\t&& _swapChain.m_supportsReadback\n\t\t\t\t&& bimg::imageConvert(bimg::TextureFormat::BGRA8, bimg::TextureFormat::Enum(_swapChain.m_colorFormat) )\n\t\t\t\t;\n\t\t}\n\n\t\ttypedef void (*SwapChainReadFunc)(void* /*src*/, uint32_t /*width*/, uint32_t /*height*/, uint32_t /*pitch*/, const void* /*userData*/);\n\n\t\tbool readSwapChain(const SwapChainVK& _swapChain, VkBuffer _buffer, VkDeviceMemory _memory, SwapChainReadFunc _func, const void* _userData = NULL)\n\t\t{\n\t\t\tif (isSwapChainReadable(_swapChain) )\n\t\t\t{\n\t\t\t\t// source for the copy is the last rendered swapchain image\n\t\t\t\tconst VkImage image = _swapChain.m_backBufferColorImage[_swapChain.m_backBufferColorIdx];\n\t\t\t\tconst VkImageLayout layout = _swapChain.m_backBufferColorImageLayout[_swapChain.m_backBufferColorIdx];\n\n\t\t\t\tconst uint32_t width  = _swapChain.m_sci.imageExtent.width;\n\t\t\t\tconst uint32_t height = _swapChain.m_sci.imageExtent.height;\n\n\t\t\t\tReadbackVK readback;\n\t\t\t\treadback.create(image, width, height, _swapChain.m_colorFormat);\n\t\t\t\tconst uint32_t pitch = readback.pitch();\n\n\t\t\t\treadback.copyImageToBuffer(m_commandBuffer, _buffer, layout, VK_IMAGE_ASPECT_COLOR_BIT);\n\n\t\t\t\t// stall for commandbuffer to finish\n\t\t\t\tkick(true);\n\n\t\t\t\tuint8_t* src;\n\t\t\t\tVK_CHECK(vkMapMemory(m_device, _memory, 0, VK_WHOLE_SIZE, 0, (void**)&src) );\n\n\t\t\t\tif (_swapChain.m_colorFormat == TextureFormat::RGBA8)\n\t\t\t\t{\n\t\t\t\t\tbimg::imageSwizzleBgra8(src, pitch, width, height, src, pitch);\n\t\t\t\t\t_func(src, width, height, pitch, _userData);\n\t\t\t\t}\n\t\t\t\telse if (_swapChain.m_colorFormat == TextureFormat::BGRA8)\n\t\t\t\t{\n\t\t\t\t\t_func(src, width, height, pitch, _userData);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tconst uint8_t dstBpp = bimg::getBitsPerPixel(bimg::TextureFormat::BGRA8);\n\t\t\t\t\tconst uint32_t dstPitch = width * dstBpp / 8;\n\t\t\t\t\tconst uint32_t dstSize = height * dstPitch;\n\n\t\t\t\t\tvoid* dst = BX_ALLOC(g_allocator, dstSize);\n\n\t\t\t\t\tbimg::imageConvert(g_allocator, dst, bimg::TextureFormat::BGRA8, src, bimg::TextureFormat::Enum(_swapChain.m_colorFormat), width, height, 1);\n\n\t\t\t\t\t_func(dst, width, height, dstPitch, _userData);\n\n\t\t\t\t\tBX_FREE(g_allocator, dst);\n\t\t\t\t}\n\n\t\t\t\tvkUnmapMemory(m_device, _memory);\n\n\t\t\t\treadback.destroy();\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tvoid capture()\n\t\t{\n\t\t\tif (m_captureSize > 0)\n\t\t\t{\n\t\t\t\tm_backBuffer.resolve();\n\n\t\t\t\tauto callback = [](void* _src, uint32_t /*_width*/, uint32_t _height, uint32_t _pitch, const void* /*_userData*/)\n\t\t\t\t{\n\t\t\t\t\tconst uint32_t size = _height * _pitch;\n\t\t\t\t\tg_callback->captureFrame(_src, size);\n\t\t\t\t};\n\n\t\t\t\treadSwapChain(m_backBuffer.m_swapChain, m_captureBuffer, m_captureMemory, callback);\n\t\t\t}\n\t\t}\n\n\t\tbool isVisible(Frame* _render, OcclusionQueryHandle _handle, bool _visible)\n\t\t{\n\t\t\treturn _visible == (0 != _render->m_occlusion[_handle.idx]);\n\t\t}\n\n\t\tvoid commit(UniformBuffer& _uniformBuffer)\n\t\t{\n\t\t\t_uniformBuffer.reset();\n\n\t\t\tfor (;;)\n\t\t\t{\n\t\t\t\tuint32_t opcode = _uniformBuffer.read();\n\t\t\tif (isValid(fbh) )\n\t\t\t{\n\t\t\t\tconst FrameBufferVK& fb = m_frameBuffers[fbh.idx];\n\t\t\t\tnumMrt = bx::max((uint8_t)1, fb.m_num);\n\t\t\t}\n\n\t\t\tVkClearAttachment attachments[BGFX_CONFIG_MAX_FRAME_BUFFERS];\n\t\t\t\tuint16_t loc;\n\t\t\t\tuint16_t num;\n\t\t\t\tuint16_t copy;\n\t\t\t\tUniformBuffer::decodeOpcode(opcode, type, loc, num, copy);\n\n\t\t\t\tconst char* data;\n\t\t\t\tif (copy)\n\t\t\t\t{\n\t\t\t\t\tdata = _uniformBuffer.read(g_uniformTypeSize[type]*num);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tUniformHandle handle;\n\t\t\t\t\tbx::memCopy(&handle, _uniformBuffer.read(sizeof(UniformHandle) ), sizeof(UniformHandle) );\n\t\t\t\t\tdata = (const char*)m_uniforms[handle.idx];\n\t\t\t\t}\n\n\t\t\t\tswitch ( (uint32_t)type)\n\t\t\t\t{\n\t\t\t\tcase UniformType::Mat3:\n\t\t\t\tcase UniformType::Mat3|kUniformFragmentBit:\n\t\t\t\t\t {\n\t\t\t\t\t\t float* value = (float*)data;\n\t\t\t\t\t\t for (uint32_t ii = 0, count = num/3; ii < count; ++ii,  loc += 3*16, value += 9)\n\t\t\t\t\t\t {\n\t\t\t\t\t\t\t Matrix4 mtx;\n\t\t\t\t\t\t\t mtx.un.val[ 0] = value[0];\n\t\t\t\t\t\t\t mtx.un.val[ 1] = value[1];\n\t\t\t\t\t\t\t mtx.un.val[ 2] = value[2];\n\t\t\t\t\t\t\t mtx.un.val[ 3] = 0.0f;\n\t\t\t\t\t\t\t mtx.un.val[ 4] = value[3];\n\t\t\t\t\t\t\t mtx.un.val[ 5] = value[4];\n\t\t\t\t\t\t\t mtx.un.val[ 6] = value[5];\n\t\t\t\t\t\t\t mtx.un.val[ 7] = 0.0f;\n\t\t\t\t\t\t\t mtx.un.val[ 8] = value[6];\n\t\t\t\t\t\t\t mtx.un.val[ 9] = value[7];\n\t\t\t\t\t\t\t mtx.un.val[10] = value[8];\n\t\t\t\t\t\t\t mtx.un.val[11] = 0.0f;\n\t\t\t\t\t\t\t setShaderUniform(uint8_t(type), loc, &mtx.un.val[0], 3);\n\t\t\t\t\t\t }\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase UniformType::Sampler:\n\t\t\t\tcase UniformType::Sampler|kUniformFragmentBit:\n\t\t\t\t\t// do nothing, but VkDescriptorSetImageInfo would be set before drawing\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase UniformType::Vec4:\n\t\t\t\tcase UniformType::Vec4 | kUniformFragmentBit:\n\t\t\t\tcase UniformType::Mat4:\n\t\t\t\tcase UniformType::Mat4 | kUniformFragmentBit:\n\t\t\t\t\t{\n\t\t\t\t\t\tsetShaderUniform(uint8_t(type), loc, data, num);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase UniformType::End:\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tBX_TRACE("%4d: INVALID 0x%08x, t %d, l %d, n %d, c %d", _uniformBuffer.getPos(), opcode, type, loc, num, copy);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvoid clearQuad(const Rect& _rect, const Clear& _clear, const float _palette[][4])\n\t\t{\n\t\t\tVkClearRect rect[1];\n\t\t\trect[0].rect.offset.x      = _rect.m_x;\n\t\t\trect[0].rect.offset.y      = _rect.m_y;\n\t\t\trect[0].rect.extent.width  = _rect.m_width;\n\t\t\trect[0].rect.extent.height = _rect.m_height;\n\t\t\trect[0].baseArrayLayer = 0;\n\t\t\trect[0].layerCount     = 1;\n\n\t\t\tuint32_t numMrt;\n\t\t\tbgfx::TextureFormat::Enum mrtFormat[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS];\n\t\t\tVkImageAspectFlags depthAspectMask;\n\n\t\t\tconst FrameBufferVK& fb = isValid(m_fbh)\n\t\t\t\t? m_frameBuffers[m_fbh.idx]\n\t\t\t\t: m_backBuffer\n\t\t\t\t;\n\n\t\t\tif (NULL == fb.m_nwh)\n\t\t\t{\n\t\t\t\tnumMrt = fb.m_num;\n\t\t\t\tfor (uint8_t ii = 0; ii < fb.m_num; ++ii)\n\t\t\t\t{\n\t\t\t\t\tmrtFormat[ii] = bgfx::TextureFormat::Enum(m_textures[fb.m_texture[ii].idx].m_requestedFormat);\n\t\t\t\t}\n\t\t\t\tdepthAspectMask = isValid(fb.m_depth) ? m_textures[fb.m_depth.idx].m_aspectMask : 0;\n\t\t\t\trect[0].layerCount = fb.m_attachment[0].numLayers;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tnumMrt = 1;\n\t\t\t\tmrtFormat[0] = fb.m_swapChain.m_colorFormat;\n\t\t\t\tdepthAspectMask = fb.m_swapChain.m_backBufferDepthStencil.m_aspectMask;\n\t\t\t}\n\n\t\t\tVkClearAttachment attachments[BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS + 1];\n\t\t\tuint32_t mrt = 0;\n\n\t\t\tif (BGFX_CLEAR_COLOR & _clear.m_flags)\n\t\t\t{\n\t\t\t\tfor (uint32_t ii = 0; ii < numMrt; ++ii)\n\t\t\t\t{\n\t\t\t\t\tattachments[mrt].colorAttachment = mrt;\n\t\t\t\t\tattachments[mrt].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n\n\t\t\t\t\tVkClearColorValue& clearValue = attachments[mrt].clearValue.color;\n\n\t\t\t\t\tconst bimg::ImageBlockInfo& blockInfo = bimg::getBlockInfo(bimg::TextureFormat::Enum(mrtFormat[ii]) );\n\t\t\t\t\tconst bx::EncodingType::Enum type = bx::EncodingType::Enum(blockInfo.encoding);\n\n\t\t\t\t\tif (BGFX_CLEAR_COLOR_USE_PALETTE & _clear.m_flags)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst uint8_t index = bx::min<uint8_t>(BGFX_CONFIG_MAX_COLOR_PALETTE-1, _clear.m_index[ii]);\n\n\t\t\t\t\t\tswitch (type)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase bx::EncodingType::Int:\n\t\t\t\t\t\tcase bx::EncodingType::Uint:\n\t\t\t\t\t\t\tclearValue.int32[0] = int32_t(_palette[index][0]);\n\t\t\t\t\t\t\tclearValue.int32[1] = int32_t(_palette[index][1]);\n\t\t\t\t\t\t\tclearValue.int32[2] = int32_t(_palette[index][2]);\n\t\t\t\t\t\t\tclearValue.int32[3] = int32_t(_palette[index][3]);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tbx::memCopy(&clearValue.float32, _palette[index], sizeof(clearValue.float32) );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tswitch (type)\n\t\t\t\t\t\t{\n\t\t\t\t\t\tcase bx::EncodingType::Int:\n\t\t\t\t\t\tcase bx::EncodingType::Uint:\n\t\t\t\t\t\t\tclearValue.uint32[0] = _clear.m_index[0];\n\t\t\t\t\t\t\tclearValue.uint32[1] = _clear.m_index[1];\n\t\t\t\t\t\t\tclearValue.uint32[2] = _clear.m_index[2];\n\t\t\t\t\t\t\tclearValue.uint32[3] = _clear.m_index[3];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tbx::unpackRgba8(clearValue.float32, _clear.m_index);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t++mrt;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdepthAspectMask &= 0\n\t\t\t\t| (_clear.m_flags & BGFX_CLEAR_DEPTH   ? VK_IMAGE_ASPECT_DEPTH_BIT   : 0)\n\t\t\t\t| (_clear.m_flags & BGFX_CLEAR_STENCIL ? VK_IMAGE_ASPECT_STENCIL_BIT : 0)\n\t\t\t\t;\n\n\t\t\tif (0 != depthAspectMask)\n\t\t\t{\n\t\t\t\tattachments[mrt].aspectMask = depthAspectMask;\n\t\t\t\tattachments[mrt].clearValue.depthStencil.stencil = _clear.m_stencil;\n\t\t\t\tattachments[mrt].clearValue.depthStencil.depth   = _clear.m_depth;\n\t\t\t\t++mrt;\n\t\t\t}\n\n\t\t\tif (mrt > 0)\n\t\t\t{\n\t\t\t\tvkCmdClearAttachments(m_commandBuffer, mrt, attachments, BX_COUNTOF(rect), rect);\n\t\t\t}\n\t\t}\n\n\t\tvoid kick(bool _finishAll = false)\n\t\t{\n\t\t\tm_cmd.kick(_finishAll);\n\t\t\tVK_CHECK(m_cmd.alloc(&m_commandBuffer) );\n\t\t\tm_cmd.finish(_finishAll);\n\t\t}\n\n\t\tint32_t selectMemoryType(uint32_t _memoryTypeBits, uint32_t _propertyFlags, int32_t _startIndex = 0) const\n\t\t{\n\t\t\tfor (int32_t ii = _startIndex, num = m_memoryProperties.memoryTypeCount; ii < num; ++ii)\n\t\t\t{\n\t\t\t\tconst VkMemoryType& memType = m_memoryProperties.memoryTypes[ii];\n\t\t\t\tif ( (0 != ( (1<<ii) & _memoryTypeBits) )\n\t\t\t\t&& ( (memType.propertyFlags & _propertyFlags) == _propertyFlags) )\n\t\t\t\t{\n\t\t\t\t\treturn ii;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tBX_TRACE("Failed to find memory that supports flags 0x%08x.", _propertyFlags);\n\t\t\treturn -1;\n\t\t}\n\n\t\tVkResult allocateMemory(const VkMemoryRequirements* requirements, VkMemoryPropertyFlags propertyFlags, ::VkDeviceMemory* memory) const\n\t\t{\n\t\t\tVkMemoryAllocateInfo ma;\n\t\t\tma.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n\t\t\tma.pNext = NULL;\n\t\t\tma.allocationSize = requirements->size;\n\n\t\t\tVkResult result = VK_ERROR_UNKNOWN;\n\t\t\tint32_t searchIndex = -1;\n\t\t\tdo\n\t\t\t{\n\t\t\t\tsearchIndex++;\n\t\t\t\tsearchIndex = selectMemoryType(requirements->memoryTypeBits, propertyFlags, searchIndex);\n\n\t\t\t\tif (searchIndex >= 0)\n\t\t\t\t{\n\t\t\t\t\tma.memoryTypeIndex = searchIndex;\n\t\t\t\t\tresult = vkAllocateMemory(m_device, &ma, m_allocatorCb, memory);\n\t\t\t\t}\n\t\tVkFence  m_fence;\n\t\tVkRenderPass m_renderPass;\n\t\tVkDescriptorPool m_descriptorPool;\n//\t\tVkDescriptorSetLayout m_descriptorSetLayout;\n//\t\tVkPipelineLayout m_pipelineLayout;\n\t\tVkPipelineCache m_pipelineCache;\n\t\tVkCommandPool m_commandPool;\n\n\n\t\tVkResult createHostBuffer(uint32_t _size, VkMemoryPropertyFlags _flags, ::VkBuffer* _buffer, ::VkDeviceMemory* _memory, const void* _data = NULL)\n\t\t{\n\t\t\tVkResult result = VK_SUCCESS;\n\n\t\t\tVkBufferCreateInfo bci;\n\t\t\tbci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;\n\t\t\tbci.pNext = NULL;\n\t\t\tbci.flags = 0;\n\t\t\tbci.size = _size;\n\t\t\tbci.queueFamilyIndexCount = 0;\n\t\t\tbci.pQueueFamilyIndices = NULL;\n\t\t\tbci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\t\tbci.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;\n\n\t\t\tresult = vkCreateBuffer(m_device, &bci, m_allocatorCb, _buffer);\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create host buffer error: vkCreateBuffer failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tVkMemoryRequirements mr;\n\t\t\tvkGetBufferMemoryRequirements(m_device, *_buffer, &mr);\n\n\n\t\tuint8_t m_fsScratch[64<<10];\n\t\tuint8_t m_vsScratch[64<<10];\n\t\tuint32_t m_fsChanges;\n\t\tuint32_t m_vsChanges;\n\n\t\tuint32_t m_backBufferColorIdx;\n\t\tFrameBufferHandle m_fbh;\n\t\t\t}\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create host buffer error: vkAllocateMemory failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tresult = vkBindBufferMemory(m_device, *_buffer, *_memory, 0);\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create host buffer error: vkBindBufferMemory failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tif (_data != NULL)\n\t\t\t{\n\t\t\t\tvoid* dst;\n\t\t\t\tresult = vkMapMemory(m_device, *_memory, 0, _size, 0, &dst);\n\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t{\n\t\t\t\t\tBX_TRACE("Create host buffer error: vkMapMemory failed %d: %s.", result, getName(result) );\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\n\t\t\t\tbx::memCopy(dst, _data, _size);\n\t\t\t\tvkUnmapMemory(m_device, *_memory);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\tVkResult createStagingBuffer(uint32_t _size, ::VkBuffer* _buffer, ::VkDeviceMemory* _memory, const void* _data = NULL)\n\t\t{\n\t\t\tconst VkMemoryPropertyFlags flags = 0\n\t\t\t\t| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n\t\t\t\t| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT\n\t\t\t\t;\n\t\t\treturn createHostBuffer(_size, flags, _buffer, _memory, _data);\n\t\t}\n\n\t\tVkResult createReadbackBuffer(uint32_t _size, ::VkBuffer* _buffer, ::VkDeviceMemory* _memory)\n\t\t{\n\t\t\tconst VkMemoryPropertyFlags flags = 0\n\t\t\t\t| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT\n\t\t\t\t| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT\n\t\t\t\t| VK_MEMORY_PROPERTY_HOST_CACHED_BIT\n\t\t\t\t;\n\t\t\treturn createHostBuffer(_size, flags, _buffer, _memory, NULL);\n\t\t}\n\n\t\tVkAllocationCallbacks*   m_allocatorCb;\n\t\tVkDebugReportCallbackEXT m_debugReportCallback;\n\t\tVkInstance       m_instance;\n\t\tVkPhysicalDevice m_physicalDevice;\n\t\tuint32_t         m_instanceApiVersion;\n\n\t\tVkPhysicalDeviceProperties       m_deviceProperties;\n\t\tVkPhysicalDeviceMemoryProperties m_memoryProperties;\n\t\tVkPhysicalDeviceFeatures         m_deviceFeatures;\n\n\t\tbool m_lineAASupport;\n\t\tbool m_borderColorSupport;\n\t\tbool m_timerQuerySupport;\n\n\t\tFrameBufferVK m_backBuffer;\n\t\tTextureFormat::Enum m_swapchainFormats[TextureFormat::Count];\n\n\t\tuint16_t m_numWindows;\n\t\tFrameBufferHandle m_windows[BGFX_CONFIG_MAX_FRAME_BUFFERS];\n\t\tint64_t m_presentElapsed;\n\n\t\tScratchBufferVK m_scratchBuffer[BGFX_CONFIG_MAX_FRAME_LATENCY];\n\n\t\tuint32_t        m_numFramesInFlight;\n\t\tCommandQueueVK  m_cmd;\n\t\tVkCommandBuffer m_commandBuffer;\n\n\t\tVkDevice m_device;\n\t\tuint32_t m_globalQueueFamily;\n\t\tVkQueue  m_globalQueue;\n\t\tVkDescriptorPool m_descriptorPool;\n\t\tVkPipelineCache  m_pipelineCache;\n\n\t\tTimerQueryVK m_gpuTimer;\n\t\tOcclusionQueryVK m_occlusionQuery;\n\n\t\tvoid* m_renderDocDll;\n\t\tvoid* m_vulkan1Dll;\n\n\t\tIndexBufferVK  m_indexBuffers[BGFX_CONFIG_MAX_INDEX_BUFFERS];\n\t\tVertexBufferVK m_vertexBuffers[BGFX_CONFIG_MAX_VERTEX_BUFFERS];\n\t\tShaderVK       m_shaders[BGFX_CONFIG_MAX_SHADERS];\n\t\tProgramVK      m_program[BGFX_CONFIG_MAX_PROGRAMS];\n\t\tTextureVK      m_textures[BGFX_CONFIG_MAX_TEXTURES];\n\t\tVertexLayout   m_vertexLayouts[BGFX_CONFIG_MAX_VERTEX_LAYOUTS];\n\t\tFrameBufferVK  m_frameBuffers[BGFX_CONFIG_MAX_FRAME_BUFFERS];\n\n\t\tvoid* m_uniforms[BGFX_CONFIG_MAX_UNIFORMS];\n\t\tMatrix4 m_predefinedUniforms[PredefinedUniform::Count];\n\t\tUniformRegistry m_uniformReg;\n\n\t\tStateCacheT<VkPipeline> m_pipelineStateCache;\n\t\tStateCacheT<VkDescriptorSetLayout> m_descriptorSetLayoutCache;\n\t\tStateCacheT<VkRenderPass> m_renderPassCache;\n\t\tStateCacheT<VkSampler> m_samplerCache;\n\t\tStateCacheT<uint32_t> m_samplerBorderColorCache;\n\t\tStateCacheLru<VkImageView, 1024> m_imageViewCache;\n\n\t\tResolution m_resolution;\n\t\tfloat m_maxAnisotropy;\n\t\tbool m_depthClamp;\n\t\tbool m_wireframe;\n\n\t\tVkBuffer m_captureBuffer;\n\t\tVkDeviceMemory m_captureMemory;\n\t\tuint32_t m_captureSize;\n\n\t\tTextVideoMem m_textVideoMem;\n\n\t\tuint8_t m_fsScratch[64<<10];\n\t\tuint8_t m_vsScratch[64<<10];\n\n\t\tFrameBufferHandle m_fbh;\n\t};\n\n\tstatic RendererContextVK* s_renderVK;\n\n\tRendererContextI* rendererCreate(const Init& _init)\n\t{\n\t\ts_renderVK = BX_NEW(g_allocator, RendererContextVK);\n\t\tif (!s_renderVK->init(_init) )\n\t\t{\n\t\t\tBX_DELETE(g_allocator, s_renderVK);\n\t\t\ts_renderVK = NULL;\n\t\t}\n\t\treturn s_renderVK;\n\t}\n\n\tvoid rendererDestroy()\n\t{\n\t\ts_renderVK->shutdown();\n\t\tBX_DELETE(g_allocator, s_renderVK);\n\t\ts_renderVK = NULL;\n\t}\n\n#define VK_DESTROY_FUNC(_name)                                                          \\\n\tvoid vkDestroy(Vk##_name& _obj)                                                     \\\n\t{                                                                                   \\\n\t\tif (VK_NULL_HANDLE != _obj)                                                     \\\n\t\t{                                                                               \\\n\t\t\tvkDestroy##_name(s_renderVK->m_device, _obj.vk, s_renderVK->m_allocatorCb); \\\n\t\t\t_obj = VK_NULL_HANDLE;                                                      \\\n\t\t}                                                                               \\\n\t}                                                                                   \\\n\tvoid release(Vk##_name& _obj)                                                       \\\n\t{                                                                                   \\\n\t\ts_renderVK->release(_obj);                                                      \\\n\t}\nVK_DESTROY\n#undef VK_DESTROY_FUNC\n\n\tvoid vkDestroy(VkDeviceMemory& _obj)\n\t{\n\t\tif (VK_NULL_HANDLE != _obj)\n\t\t{\n\t\t\tvkFreeMemory(s_renderVK->m_device, _obj.vk, s_renderVK->m_allocatorCb);\n\t\t\t_obj = VK_NULL_HANDLE;\n\t\t}\n\t}\n\n\tvoid vkDestroy(VkSurfaceKHR& _obj)\n\t{\n\t\tif (VK_NULL_HANDLE != _obj)\n\t\t{\n\t\t\tvkDestroySurfaceKHR(s_renderVK->m_instance, _obj.vk, s_renderVK->m_allocatorCb);\n\t\t\t_obj = VK_NULL_HANDLE;\n\t\t}\n\t}\n\n\tvoid vkDestroy(VkDescriptorSet& _obj)\n\t{\n\t\tif (VK_NULL_HANDLE != _obj)\n\t\t{\n\t\t\tvkFreeDescriptorSets(s_renderVK->m_device, s_renderVK->m_descriptorPool, 1, &_obj);\n\t\t\t_obj = VK_NULL_HANDLE;\n\t\t}\n\t}\n\n\tvoid release(VkDeviceMemory& _obj)\n\t{\n\t\ts_renderVK->release(_obj);\n\t}\n\n\tvoid release(VkSurfaceKHR& _obj)\n\t{\n\t\ts_renderVK->release(_obj);\n\t}\n\n\tvoid release(VkDescriptorSet& _obj)\n\t{\n\t\ts_renderVK->release(_obj);\n\t}\n\n\tvoid ScratchBufferVK::create(uint32_t _size, uint32_t _count)\n\t{\n\t\tconst VkAllocationCallbacks* allocatorCb = s_renderVK->m_allocatorCb;\n\t\tconst VkDevice device = s_renderVK->m_device;\n\t\tconst VkPhysicalDeviceLimits& deviceLimits = s_renderVK->m_deviceProperties.limits;\n\n\t\tconst uint32_t align = uint32_t(deviceLimits.minUniformBufferOffsetAlignment);\n\t\tconst uint32_t entrySize = bx::strideAlign(_size, align);\n\t\tconst uint32_t totalSize = entrySize * _count;\n\n\t\tVkBufferCreateInfo bci;\n\t\tbci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;\n\t\tbci.pNext = NULL;\n\t\tbci.flags = 0;\n\t\tbci.size  = totalSize;\n\t\tbci.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;\n\t\tbci.sharingMode           = VK_SHARING_MODE_EXCLUSIVE;\n\t\tbci.queueFamilyIndexCount = 0;\n\t\tbci.pQueueFamilyIndices   = NULL;\n\n\t\tVK_CHECK(vkCreateBuffer(\n\t\t\t  device\n\t\t\t, &bci\n\t\t\t, allocatorCb\n\t\t\t, &m_buffer\n\t\t\t) );\n\n\t\tVkMemoryRequirements mr;\n\t\tvkGetBufferMemoryRequirements(\n\t\t\t  device\n\t\t\t, m_buffer\n\t\t\t, &mr\n\t\t\t);\n\n\t\tVkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\t\tVkResult result = s_renderVK->allocateMemory(&mr, flags, &m_deviceMem);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tflags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;\n\t\t\tVK_CHECK(s_renderVK->allocateMemory(&mr, flags, &m_deviceMem) );\n\t\t}\n\n\t\tm_size = (uint32_t)mr.size;\n\t\tm_pos  = 0;\n\n\t\tVK_CHECK(vkBindBufferMemory(device, m_buffer, m_deviceMem, 0) );\n\n\t\tVK_CHECK(vkMapMemory(device, m_deviceMem, 0, m_size, 0, (void**)&m_data) );\n\t}\n\n\tvoid ScratchBufferVK::destroy()\n\t{\n\t\treset();\n\n\t\tvkUnmapMemory(s_renderVK->m_device, m_deviceMem);\n\n\t\ts_renderVK->release(m_buffer);\n\t\ts_renderVK->release(m_deviceMem);\n\t}\n\n\tvoid ScratchBufferVK::reset()\n\t{\n\t\tm_pos = 0;\n\t}\n\n\tuint32_t ScratchBufferVK::write(const void* _data, uint32_t _size)\n\t{\n\t\tBX_ASSERT(m_pos < m_size, "Out of scratch buffer memory");\n\n\t\tconst uint32_t offset = m_pos;\n\n\t\tif (_size > 0)\n\t\t{\n\t\t\tbx::memCopy(&m_data[m_pos], _data, _size);\n\n\t\t\tconst VkPhysicalDeviceLimits& deviceLimits = s_renderVK->m_deviceProperties.limits;\n\t\t\tconst uint32_t align = uint32_t(deviceLimits.minUniformBufferOffsetAlignment);\n\t\t\tconst uint32_t alignedSize = bx::strideAlign(_size, align);\n\n\t\t\tm_pos += alignedSize;\n\t\t}\n\n\t\treturn offset;\n\t}\n\n\tvoid ScratchBufferVK::flush()\n\t{\n\t\tconst VkPhysicalDeviceLimits& deviceLimits = s_renderVK->m_deviceProperties.limits;\n\t\tVkDevice device = s_renderVK->m_device;\n\n\t\tconst uint32_t align = uint32_t(deviceLimits.nonCoherentAtomSize);\n\t\tconst uint32_t size  = bx::min(bx::strideAlign(m_pos, align), m_size);\n\n\t\tVkMappedMemoryRange range;\n\t\trange.sType  = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;\n\t\trange.pNext  = NULL;\n\t\trange.memory = m_deviceMem;\n\t\trange.offset = 0;\n\t\trange.size   = size;\n\t\tVK_CHECK(vkFlushMappedMemoryRanges(device, 1, &range) );\n\t}\n\n\tvoid BufferVK::create(VkCommandBuffer _commandBuffer, uint32_t _size, void* _data, uint16_t _flags, bool _vertex, uint32_t _stride)\n\t{\n\t\tBX_UNUSED(_stride);\n\n\t\tm_size    = _size;\n\t\tm_flags   = _flags;\n\t\tm_dynamic = NULL == _data;\n\n\t\tconst bool storage  = m_flags & BGFX_BUFFER_COMPUTE_READ_WRITE;\n\t\tconst bool indirect = m_flags & BGFX_BUFFER_DRAW_INDIRECT;\n\n\t\tVkBufferCreateInfo bci;\n\t\tbci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;\n\t\tbci.pNext = NULL;\n\t\tbci.flags = 0;\n\t\tbci.size  = _size;\n\t\tbci.usage = 0\n\t\t\t| (_vertex              ? VK_BUFFER_USAGE_VERTEX_BUFFER_BIT   : VK_BUFFER_USAGE_INDEX_BUFFER_BIT)\n\t\t\t| (storage || indirect  ? VK_BUFFER_USAGE_STORAGE_BUFFER_BIT  : 0)\n\t\t\t| (indirect             ? VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT : 0)\n\t\t\t| VK_BUFFER_USAGE_TRANSFER_DST_BIT\n\t\t\t;\n\t\tbci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\t\tbci.queueFamilyIndexCount = 0;\n\t\tbci.pQueueFamilyIndices   = NULL;\n\n\t\tconst VkAllocationCallbacks* allocatorCb = s_renderVK->m_allocatorCb;\n\t\tconst VkDevice device = s_renderVK->m_device;\n\t\tVK_CHECK(vkCreateBuffer(device, &bci, allocatorCb, &m_buffer) );\n\n\t\tVkMemoryRequirements mr;\n\t\tvkGetBufferMemoryRequirements(device, m_buffer, &mr);\n\n\t\tVK_CHECK(s_renderVK->allocateMemory(&mr, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &m_deviceMem) );\n\n\t\tVK_CHECK(vkBindBufferMemory(device, m_buffer, m_deviceMem, 0) );\n\n\t\tif (!m_dynamic)\n\t\t{\n\t\t\tupdate(_commandBuffer, 0, _size, _data);\n\t\t}\n\t}\n\n\tvoid BufferVK::update(VkCommandBuffer _commandBuffer, uint32_t _offset, uint32_t _size, void* _data, bool _discard)\n\t{\n\t\tBX_UNUSED(_discard);\n\n\t\tVkBuffer stagingBuffer;\n\t\tVkDeviceMemory stagingMem;\n\t\tVK_CHECK(s_renderVK->createStagingBuffer(_size, &stagingBuffer, &stagingMem, _data) );\n\n\t\tVkBufferCopy region;\n\t\tregion.srcOffset = 0;\n\t\tregion.dstOffset = _offset;\n\t\tregion.size      = _size;\n\t\tvkCmdCopyBuffer(_commandBuffer, stagingBuffer, m_buffer, 1, &region);\n\n\t\tsetMemoryBarrier(\n\t\t\t  _commandBuffer\n\t\t\t, VK_PIPELINE_STAGE_TRANSFER_BIT\n\t\t\t, VK_PIPELINE_STAGE_TRANSFER_BIT\n\t\t\t);\n\n\t\ts_renderVK->release(stagingBuffer);\n\t\ts_renderVK->release(stagingMem);\n\t}\n\n\tvoid BufferVK::destroy()\n\t{\n\t\tif (VK_NULL_HANDLE != m_buffer)\n\t\t{\n\t\t\ts_renderVK->release(m_buffer);\n\t\t\ts_renderVK->release(m_deviceMem);\n\n\t\t\tm_dynamic = false;\n\t\t}\n\t}\n\n\tvoid VertexBufferVK::create(VkCommandBuffer _commandBuffer, uint32_t _size, void* _data, VertexLayoutHandle _layoutHandle, uint16_t _flags)\n\t{\n\t\tBufferVK::create(_commandBuffer, _size, _data, _flags, true);\n\t\tm_layoutHandle = _layoutHandle;\n\t}\n\n\tvoid ShaderVK::create(const Memory* _mem)\n\t{\n\t\tbx::MemoryReader reader(_mem->data, _mem->size);\n\n\t\tbx::ErrorAssert err;\n\n\t\tuint32_t magic;\n\t\tbx::read(&reader, magic, &err);\n\n\t\tVkShaderStageFlagBits shaderStage = VK_SHADER_STAGE_ALL;\n\n\t\tif (isShaderType(magic, \'C\') )\n\t\t{\n\t\t\tshaderStage = VK_SHADER_STAGE_COMPUTE_BIT;\n\t\t}\n\t\telse if (isShaderType(magic, \'F\') )\n\t\t{\n\t\t\tshaderStage = VK_SHADER_STAGE_FRAGMENT_BIT;\n\t\t}\n\t\telse if (isShaderType(magic, \'V\') )\n\t\t{\n\t\t\tshaderStage = VK_SHADER_STAGE_VERTEX_BIT;\n\t\t}\n\n\t\tconst bool fragment = isShaderType(magic, \'F\');\n\n\t\tuint32_t hashIn;\n\t\tbx::read(&reader, hashIn, &err);\n\n\t\tuint32_t hashOut;\n\n\t\tif (isShaderVerLess(magic, 6) )\n\t\t{\n\t\t\thashOut = hashIn;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tbx::read(&reader, hashOut, &err);\n\t\t}\n\n\t\tuint16_t count;\n\t\tbx::read(&reader, count, &err);\n\n\t\tm_numPredefined = 0;\n\t\tm_numUniforms   = count;\n\t\tm_numTextures   = 0;\n\n\t\tm_oldBindingModel = isShaderVerLess(magic, 11);\n\n\t\tBX_TRACE("%s Shader consts %d"\n\t\t\t, getShaderTypeName(magic)\n\t\t\t, count\n\t\t\t);\n\n\t\tuint8_t fragmentBit = fragment ? kUniformFragmentBit : 0;\n\n\t\tfor (uint32_t ii = 0; ii < BGFX_CONFIG_MAX_TEXTURE_SAMPLERS; ++ii)\n\t\t{\n\t\t\tm_bindInfo[ii].uniformHandle  = BGFX_INVALID_HANDLE;\n\t\t\tm_bindInfo[ii].type           = BindType::Count;\n\t\t\tm_bindInfo[ii].binding        = 0;\n\t\t\tm_bindInfo[ii].samplerBinding = 0;\n\t\t\tm_bindInfo[ii].index          = UINT32_MAX;\n\t\t}\n\n\t\tif (0 < count)\n\t\t{\n\t\t\tfor (uint32_t ii = 0; ii < count; ++ii)\n\t\t\t{\n\t\t\t\tuint8_t nameSize = 0;\n\t\t\t\tbx::read(&reader, nameSize, &err);\n\n\t\t\t\tchar name[256];\n\t\t\t\tbx::read(&reader, &name, nameSize, &err);\n\t\t\t\tname[nameSize] = \'\\0\';\n\n\t\t\t\tuint8_t type = 0;\n\t\t\t\tbx::read(&reader, type, &err);\n\n\t\t\t\tuint8_t num;\n\t\t\t\tbx::read(&reader, num, &err);\n\n\t\t\t\tuint16_t regIndex;\n\t\t\t\tbx::read(&reader, regIndex, &err);\n\n\t\t\t\tuint16_t regCount;\n\t\t\t\tbx::read(&reader, regCount, &err);\n\n\t\t\t\tconst bool hasTexData   = !isShaderVerLess(magic, 8);\n\t\t\t\tconst bool hasTexFormat = !isShaderVerLess(magic, 10);\n\t\t\t\tuint8_t  texComponent   = 0;\n\t\t\t\tuint8_t  texDimension   = 0;\n\t\t\t\tuint16_t texFormat      = 0;\n\n\t\t\t\tif (hasTexData)\n\t\t\t\t{\n\t\t\t\t\tbx::read(&reader, texComponent, &err);\n\t\t\t\t\tbx::read(&reader, texDimension, &err);\n\t\t\t\t}\n\n\t\t\t\tif (hasTexFormat)\n\t\t\t\t{\n\t\t\t\t\tbx::read(&reader, texFormat, &err);\n\t\t\t\t}\n\n\t\t\t\tconst char* kind = "invalid";\n\n\t\t\t\tBX_UNUSED(num, texComponent, texFormat);\n\n\t\t\t\tauto textureDimensionToViewType = [](TextureDimension::Enum dimension)\n\t\t\t\t{\n\t\t\t\t\tswitch (dimension)\n\t\t\t\t\t{\n\t\t\t\t\tcase TextureDimension::Dimension1D:        return VK_IMAGE_VIEW_TYPE_1D;\n\t\t\t\t\tcase TextureDimension::Dimension2D:        return VK_IMAGE_VIEW_TYPE_2D;\n\t\t\t\t\tcase TextureDimension::Dimension2DArray:   return VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n\t\t\t\t\tcase TextureDimension::DimensionCube:      return VK_IMAGE_VIEW_TYPE_CUBE;\n\t\t\t\t\tcase TextureDimension::DimensionCubeArray: return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;\n\t\t\t\t\tcase TextureDimension::Dimension3D:        return VK_IMAGE_VIEW_TYPE_3D;\n\t\t\t\t\tdefault:                                   return VK_IMAGE_VIEW_TYPE_MAX_ENUM;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tif (UINT16_MAX != regIndex)\n\t\t\t\t{\n\t\t\t\t\tPredefinedUniform::Enum predefined = nameToPredefinedUniformEnum(name);\n\t\t\t\t\tif (PredefinedUniform::Count != predefined)\n\t\t\t\t\t{\n\t\t\t\t\t\tkind = "predefined";\n\t\t\t\t\t\tm_predefined[m_numPredefined].m_loc   = regIndex;\n\t\t\t\t\t\tm_predefined[m_numPredefined].m_count = regCount;\n\t\t\t\t\t\tm_predefined[m_numPredefined].m_type  = uint8_t(predefined|fragmentBit);\n\t\t\t\t\t\tm_numPredefined++;\n\t\t\t\t\t}\n\t\t\t\t\telse if (UniformType::End == (~kUniformMask & type) )\n\t\t\t\t\t{\n\t\t\t\t\t\t// regCount is used for descriptor type\n\t\t\t\t\t\tconst bool isBuffer = idToDescriptorType(regCount) == DescriptorType::StorageBuffer;\n\t\t\t\t\t\tif (0 == regIndex)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst uint8_t reverseShift = m_oldBindingModel\n\t\t\t\t\t\t\t? (fragment ? kSpirvOldFragmentShift : 0) + (isBuffer ? kSpirvOldBufferShift : kSpirvOldImageShift)\n\t\t\t\t\t\t\t: kSpirvBindShift;\n\n\t\t\t\t\t\tconst uint16_t stage = regIndex - reverseShift; // regIndex is used for buffer binding index\n\n\t\t\t\t\t\tm_bindInfo[stage].type = isBuffer ? BindType::Buffer : BindType::Image;\n\t\t\t\t\t\tm_bindInfo[stage].uniformHandle  = { 0 };\n\t\t\t\t\t\tm_bindInfo[stage].binding        = regIndex;\n\n\t\t\t\t\t\tif (!isBuffer)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst VkImageViewType viewType = hasTexData\n\t\t\t\t\t\t\t\t? textureDimensionToViewType(idToTextureDimension(texDimension) )\n\t\t\t\t\t\t\t\t: VK_IMAGE_VIEW_TYPE_MAX_ENUM\n\t\t\t\t\t\t\t\t;\n\n\t\t\t\t\t\t\tif (VK_IMAGE_VIEW_TYPE_MAX_ENUM != viewType)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tm_bindInfo[stage].index = m_numTextures;\n\t\t\t\t\t\t\t\tm_textures[m_numTextures].type = viewType;\n\t\t\t\t\t\t\t\tm_numTextures++;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tkind = "storage";\n\t\t\t\t\t}\n\t\t\t\t\telse if (UniformType::Sampler == (~kUniformMask & type) )\n\t\t\t\t\t{\n\t\t\t\t\t\tconst uint8_t reverseShift = m_oldBindingModel\n\t\t\t\t\t\t\t? (fragment ? kSpirvOldFragmentShift : 0) + kSpirvOldTextureShift\n\t\t\t\t\t\t\t: kSpirvBindShift;\n\n\t\t\t\t\t\tconst uint16_t stage = regIndex - reverseShift; // regIndex is used for image/sampler binding index\n\n\t\t\t\t\t\tconst UniformRegInfo* info = s_renderVK->m_uniformReg.find(name);\n\t\t\t\t\t\tBX_ASSERT(NULL != info, "User defined uniform \'%s\' is not found, it won\'t be set.", name);\n\n\t\t\t\t\t\tm_bindInfo[stage].uniformHandle    = info->m_handle;\n\t\t\t\t\t\tm_bindInfo[stage].type             = BindType::Sampler;\n\t\t\t\t\t\tm_bindInfo[stage].binding          = regIndex;\n\t\t\t\t\t\tm_bindInfo[stage].samplerBinding   = regIndex + kSpirvSamplerShift;\n\n\t\t\t\t\t\tconst VkImageViewType viewType = hasTexData\n\t\t\t\t\t\t\t? textureDimensionToViewType(idToTextureDimension(texDimension) )\n\t\t\t\t\t\t\t: VK_IMAGE_VIEW_TYPE_MAX_ENUM\n\t\t\t\t\t\t\t;\n\n\t\t\t\t\t\tif (VK_IMAGE_VIEW_TYPE_MAX_ENUM != viewType)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tm_bindInfo[stage].index = m_numTextures;\n\t\t\t\t\t\t\tm_textures[m_numTextures].type = viewType;\n\t\t\t\t\t\t\tm_numTextures++;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tkind = "sampler";\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tconst UniformRegInfo* info = s_renderVK->m_uniformReg.find(name);\n\t\t\t\t\t\tBX_ASSERT(NULL != info, "User defined uniform \'%s\' is not found, it won\'t be set.", name);\n\n\t\t\t\t\t\tif (NULL != info)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (NULL == m_constantBuffer)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tm_constantBuffer = UniformBuffer::create(1024);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tkind = "user";\n\t\t\t\t\t\t\tm_constantBuffer->writeUniformHandle( (UniformType::Enum)(type|fragmentBit), regIndex, info->m_handle, regCount);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tBX_TRACE("\\t%s: %s (%s), r.index %3d, r.count %2d, r.texComponent %1d, r.texDimension %1d"\n\t\t\t\t\t, kind\n\t\t\t\t\t, name\n\t\t\t\t\t, getUniformTypeName(UniformType::Enum(type&~kUniformMask) )\n\t\t\t\t\t, regIndex\n\t\t\t\t\t, regCount\n\t\t\t\t\t, texComponent\n\t\t\t\t\t, texDimension\n\t\t\t\t\t);\n\t\t\t\tBX_UNUSED(kind);\n\t\t\t}\n\n\t\t\tif (NULL != m_constantBuffer)\n\t\t\t{\n\t\t\t\tm_constantBuffer->finish();\n\t\t\t}\n\t\t}\n\n\t\tuint32_t shaderSize;\n\t\tbx::read(&reader, shaderSize, &err);\n\n\t\tconst void* code = reader.getDataPtr();\n\t\tbx::skip(&reader, shaderSize+1);\n\n\t\tm_code = alloc(shaderSize);\n\t\tbx::memCopy(m_code->data, code, shaderSize);\n\n\t\tVkShaderModuleCreateInfo smci;\n\t\tsmci.sType    = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;\n\t\tsmci.pNext    = NULL;\n\t\tsmci.flags    = 0;\n\t\tsmci.codeSize = m_code->size;\n\t\tsmci.pCode    = (const uint32_t*)m_code->data;\n\n//\t\tdisassemble(bx::getDebugOut(), m_code->data, m_code->size);\n\n\t\tVK_CHECK(vkCreateShaderModule(\n\t\t\t  s_renderVK->m_device\n\t\t\t, &smci\n\t\t\t, s_renderVK->m_allocatorCb\n\t\t\t, &m_module\n\t\t\t) );\n\n\t\tbx::memSet(m_attrMask,  0, sizeof(m_attrMask) );\n\t\tbx::memSet(m_attrRemap, 0, sizeof(m_attrRemap) );\n\n\t\tbx::read(&reader, m_numAttrs, &err);\n\n\t\tfor (uint8_t ii = 0; ii < m_numAttrs; ++ii)\n\t\t{\n\t\t\tuint16_t id;\n\t\t\tbx::read(&reader, id, &err);\n\n\t\t\tAttrib::Enum attr = idToAttrib(id);\n\n\t\t\tif (Attrib::Count != attr)\n\t\t\t{\n\t\t\t\tm_attrMask[attr]  = UINT16_MAX;\n\t\t\t\tm_attrRemap[attr] = ii;\n\t\t\t}\n\t\t}\n\n\t\tbx::HashMurmur2A murmur;\n\t\tmurmur.begin();\n\t\tmurmur.add(hashIn);\n\t\tmurmur.add(hashOut);\n\t\tmurmur.add(m_code->data, m_code->size);\n\t\tmurmur.add(m_numAttrs);\n\t\tmurmur.add(m_attrMask,  m_numAttrs);\n\t\tmurmur.add(m_attrRemap, m_numAttrs);\n\t\tm_hash = murmur.end();\n\n\t\tbx::read(&reader, m_size, &err);\n\n\t\t// fill binding description with uniform information\n\t\tuint16_t bidx = 0;\n\t\tif (m_size > 0)\n\t\t{\n\t\t\tm_uniformBinding = fragment ? (m_oldBindingModel ? kSpirvOldFragmentBinding : kSpirvFragmentBinding) : 0;\n\n\t\t\tVkDescriptorSetLayoutBinding& binding = m_bindings[bidx];\n\t\t\tbinding.stageFlags = shaderStage;\n\t\t\tbinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;\n\t\t\tbinding.binding = m_uniformBinding;\n\t\t\tbinding.pImmutableSamplers = NULL;\n\t\t\tbinding.descriptorCount = 1;\n\t\t\tbidx++;\n\t\t}\n\n\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_bindInfo); ++ii)\n\t\t{\n\t\t\tswitch (m_bindInfo[ii].type)\n\t\t\t{\n\t\t\t\tcase BindType::Buffer:\n\t\t\t\tcase BindType::Image:\n\t\t\t\t{\n\t\t\t\t\tVkDescriptorSetLayoutBinding& binding = m_bindings[bidx];\n\t\t\t\t\tbinding.stageFlags = shaderStage;\n\t\t\t\t\tbinding.descriptorType = BindType::Buffer == m_bindInfo[ii].type\n\t\t\t\t\t\t? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER\n\t\t\t\t\t\t: VK_DESCRIPTOR_TYPE_STORAGE_IMAGE\n\t\t\t\t\t\t;\n\t\t\t\t\tbinding.binding = m_bindInfo[ii].binding;\n\t\t\t\t\tbinding.pImmutableSamplers = NULL;\n\t\t\t\t\tbinding.descriptorCount = 1;\n\t\t\t\t\tbidx++;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\t\tcase BindType::Sampler:\n\t\t\t\t{\n\t\t\t\t\tVkDescriptorSetLayoutBinding& textureBinding = m_bindings[bidx];\n\t\t\t\t\ttextureBinding.stageFlags = shaderStage;\n\t\t\t\t\ttextureBinding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;\n\t\t\t\t\ttextureBinding.binding = m_bindInfo[ii].binding;\n\t\t\t\t\ttextureBinding.pImmutableSamplers = NULL;\n\t\t\t\t\ttextureBinding.descriptorCount = 1;\n\t\t\t\t\tbidx++;\n\n\t\t\t\t\tVkDescriptorSetLayoutBinding& samplerBinding = m_bindings[bidx];\n\t\t\t\t\tsamplerBinding.stageFlags = shaderStage;\n\t\t\t\t\tsamplerBinding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;\n\t\t\t\t\tsamplerBinding.binding = m_bindInfo[ii].samplerBinding;\n\t\t\t\t\tsamplerBinding.pImmutableSamplers = NULL;\n\t\t\t\t\tsamplerBinding.descriptorCount = 1;\n\t\t\t\t\tbidx++;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tm_numBindings = bidx;\n\t}\n\n\tvoid ShaderVK::destroy()\n\t{\n\t\tif (NULL != m_constantBuffer)\n\t\t{\n\t\t\tUniformBuffer::destroy(m_constantBuffer);\n\t\t\tm_constantBuffer = NULL;\n\t\t}\n\n\t\tm_numPredefined = 0;\n\n\t\tif (NULL != m_code)\n\t\t{\n\t\t\trelease(m_code);\n\t\t\tm_code = NULL;\n\t\t\tm_hash = 0;\n\t\t}\n\n\t\tif (VK_NULL_HANDLE != m_module)\n\t\t{\n\t\t\tvkDestroy(m_module);\n\t\t}\n\t}\n\n\tvoid ProgramVK::create(const ShaderVK* _vsh, const ShaderVK* _fsh)\n\t{\n\t\tBX_ASSERT(NULL != _vsh->m_code, "Vertex shader doesn\'t exist.");\n\t\tm_vsh = _vsh;\n\t\tbx::memCopy(\n\t\t\t  &m_predefined[0]\n\t\t\t, _vsh->m_predefined\n\t\t\t, _vsh->m_numPredefined * sizeof(PredefinedUniform)\n\t\t\t);\n\t\tm_numPredefined = _vsh->m_numPredefined;\n\n\t\tif (NULL != _fsh)\n\t\t{\n\t\t\tBX_ASSERT(NULL != _fsh->m_code, "Fragment shader doesn\'t exist.");\n\t\t\tm_fsh = _fsh;\n\t\t\tbx::memCopy(\n\t\t\t\t  &m_predefined[m_numPredefined]\n\t\t\t\t, _fsh->m_predefined\n\t\t\t\t, _fsh->m_numPredefined * sizeof(PredefinedUniform)\n\t\t\t\t);\n\t\t\tm_numPredefined += _fsh->m_numPredefined;\n\t\t}\n\n\t\tm_numTextures = 0;\n\n\t\tfor (uint8_t stage = 0; stage < BX_COUNTOF(m_bindInfo); ++stage)\n\t\t{\n\t\t\tconst ShaderVK* shader = NULL;\n\t\t\tif (isValid(m_vsh->m_bindInfo[stage].uniformHandle) )\n\t\t\t{\n\t\t\t\tshader = _vsh;\n\n\t\t\t\tBX_ASSERT(false\n\t\t\t\t\t  || NULL == m_fsh\n\t\t\t\t\t  || !isValid(m_fsh->m_bindInfo[stage].uniformHandle)\n\t\t\t\t\t  || !(m_vsh->m_oldBindingModel || m_fsh->m_oldBindingModel)\n\t\t\t\t\t, "Shared vertex/fragment bindings require shader binary version >= 11."\n\t\t\t\t\t);\n\t\t\t}\n\t\t\telse if (NULL != m_fsh\n\t\t\t\t &&  isValid(m_fsh->m_bindInfo[stage].uniformHandle) )\n\t\t\t{\n\t\t\t\tshader = _fsh;\n\t\t\t}\n\n\t\t\tif (NULL != shader)\n\t\t\t{\n\t\t\t\tm_bindInfo[stage] = shader->m_bindInfo[stage];\n\t\t\t\tuint32_t& index = m_bindInfo[stage].index;\n\t\t\t\tif (UINT32_MAX != index)\n\t\t\t\t{\n\t\t\t\t\tm_textures[m_numTextures] = shader->m_textures[index];\n\t\t\t\t\tindex = m_numTextures;\n\t\t\t\t\tm_numTextures++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// create exact pipeline layout\n\t\tm_descriptorSetLayout = VK_NULL_HANDLE;\n\n\t\tuint32_t numBindings = m_vsh->m_numBindings + (m_fsh ? m_fsh->m_numBindings : 0);\n\t\tif (0 < numBindings)\n\t\t{\n\t\t\t// generate descriptor set layout hash\n\t\t\tbx::HashMurmur2A murmur;\n\t\t\tmurmur.begin();\n\t\t\tmurmur.add(m_vsh->m_bindings, sizeof(VkDescriptorSetLayoutBinding) * m_vsh->m_numBindings);\n\n\t\t\tif (NULL != m_fsh)\n\t\t\t{\n\t\t\t\tmurmur.add(m_fsh->m_bindings, sizeof(VkDescriptorSetLayoutBinding) * m_fsh->m_numBindings);\n\t\t\t}\n\n\t\t\tuint32_t descriptorSetLayoutHash = murmur.end();\n\n\t\t\tm_descriptorSetLayout = s_renderVK->m_descriptorSetLayoutCache.find(descriptorSetLayoutHash);\n\n\t\t\tif (VK_NULL_HANDLE == m_descriptorSetLayout)\n\t\t\t{\n\t\t\t\tVkDescriptorSetLayoutBinding bindings[2 * BX_COUNTOF(ShaderVK::m_bindings)];\n\n\t\t\t\tbx::memCopy(\n\t\t\t\t\t  bindings\n\t\t\t\t\t, m_vsh->m_bindings\n\t\t\t\t\t, sizeof(VkDescriptorSetLayoutBinding) * m_vsh->m_numBindings\n\t\t\t\t\t);\n\n\t\t\t\tnumBindings = m_vsh->m_numBindings;\n\n\t\t\t\tif (NULL != m_fsh)\n\t\t\t\t{\n\t\t\t\t\tfor (uint16_t ii = 0; ii < m_fsh->m_numBindings; ii++)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst VkDescriptorSetLayoutBinding& fsBinding = m_fsh->m_bindings[ii];\n\t\t\t\t\t\tuint16_t vsBindingIdx = UINT16_MAX;\n\t\t\t\t\t\tfor (uint16_t jj = 0; jj < m_vsh->m_numBindings; jj++)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (fsBinding.binding == bindings[jj].binding)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tvsBindingIdx = jj;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (UINT16_MAX != vsBindingIdx)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tBX_ASSERT(\n\t\t\t\t\t\t\t\t  bindings[vsBindingIdx].descriptorType == fsBinding.descriptorType\n\t\t\t\t\t\t\t\t, "Mismatching descriptor types. Shaders compiled with different versions of shaderc?"\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tbindings[vsBindingIdx].stageFlags |= fsBinding.stageFlags;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbindings[numBindings] = fsBinding;\n\t\t\t\t\t\t\tnumBindings++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tVkDescriptorSetLayoutCreateInfo dslci;\n\t\t\t\tdslci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;\n\t\t\t\tdslci.pNext = NULL;\n\t\t\t\tdslci.flags = 0;\n\t\t\t\tdslci.bindingCount = numBindings;\n\t\t\t\tdslci.pBindings = bindings;\n\n\t\t\t\tVK_CHECK(vkCreateDescriptorSetLayout(\n\t\t\t\t\t  s_renderVK->m_device\n\t\t\t\t\t, &dslci\n\t\t\t\t\t, s_renderVK->m_allocatorCb\n\t\t\t\t\t, &m_descriptorSetLayout\n\t\t\t\t\t) );\n\n\t\t\t\ts_renderVK->m_descriptorSetLayoutCache.add(descriptorSetLayoutHash, m_descriptorSetLayout);\n\t\t\t}\n\t\t}\n\n\t\tVkPipelineLayoutCreateInfo plci;\n\t\tplci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;\n\t\tplci.pNext = NULL;\n\t\tplci.flags = 0;\n\t\tplci.pushConstantRangeCount = 0;\n\t\tplci.pPushConstantRanges = NULL;\n\t\tplci.setLayoutCount = (m_descriptorSetLayout == VK_NULL_HANDLE ? 0 : 1);\n\t\tplci.pSetLayouts = &m_descriptorSetLayout;\n\n\t\tVK_CHECK(vkCreatePipelineLayout(\n\t\t\t  s_renderVK->m_device\n\t\t\t, &plci\n\t\t\t, s_renderVK->m_allocatorCb\n\t\t\t, &m_pipelineLayout\n\t\t\t) );\n\t}\n\n\tvoid ProgramVK::destroy()\n\t{\n\t\ts_renderVK->release(m_pipelineLayout);\n\t\tm_numPredefined = 0;\n\t\tm_vsh = NULL;\n\t\tm_fsh = NULL;\n\t}\n\n\tVkResult TimerQueryVK::init()\n\t{\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tconst VkDevice device = s_renderVK->m_device;\n\t\tconst VkCommandBuffer commandBuffer = s_renderVK->m_commandBuffer;\n\n\t\tconst uint32_t count = m_control.m_size * 2;\n\n\t\tVkQueryPoolCreateInfo qpci;\n\t\tqpci.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;\n\t\tqpci.pNext = NULL;\n\t\tqpci.flags = 0;\n\t\tqpci.queryType = VK_QUERY_TYPE_TIMESTAMP;\n\t\tqpci.queryCount = count;\n\t\tqpci.pipelineStatistics = 0;\n\n\t\tresult = vkCreateQueryPool(device, &qpci, s_renderVK->m_allocatorCb, &m_queryPool);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create timer query error: vkCreateQueryPool failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tvkCmdResetQueryPool(commandBuffer, m_queryPool, 0, count);\n\n\t\tconst uint32_t size = count * sizeof(uint64_t);\n\t\tresult = s_renderVK->createReadbackBuffer(size, &m_readback, &m_readbackMemory);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\treturn result;\n\t\t}\n\n\t\tresult = vkMapMemory(device, m_readbackMemory, 0, VK_WHOLE_SIZE, 0, (void**)&m_queryResult);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create timer query error: vkMapMemory failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tm_frequency = uint64_t(1000000000.0 / double(s_renderVK->m_deviceProperties.limits.timestampPeriod) );\n\n\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_result); ++ii)\n\t\t{\n\t\t\tm_result[ii].reset();\n\t\t}\n\n\t\tm_control.reset();\n\n\t\treturn result;\n\t}\n\n\tvoid TimerQueryVK::shutdown()\n\t{\n\t\tvkDestroy(m_queryPool);\n\t\tvkDestroy(m_readback);\n\t\tvkUnmapMemory(s_renderVK->m_device, m_readbackMemory);\n\t\tvkDestroy(m_readbackMemory);\n\t}\n\n\tuint32_t TimerQueryVK::begin(uint32_t _resultIdx, uint32_t _frameNum)\n\t{\n\t\twhile (0 == m_control.reserve(1) )\n\t\t{\n\t\t\tm_control.consume(1);\n\t\t}\n\n\t\tResult& result = m_result[_resultIdx];\n\t\t++result.m_pending;\n\n\t\tconst uint32_t idx = m_control.m_current;\n\t\tQuery& query = m_query[idx];\n\t\tquery.m_resultIdx = _resultIdx;\n\t\tquery.m_ready     = false;\n\t\tquery.m_frameNum  = _frameNum;\n\n\t\tconst VkCommandBuffer commandBuffer = s_renderVK->m_commandBuffer;\n\t\tconst uint32_t offset = idx * 2 + 0;\n\n\t\tvkCmdResetQueryPool(commandBuffer, m_queryPool, offset, 2);\n\t\tvkCmdWriteTimestamp(commandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, m_queryPool, offset + 0);\n\n\t\tm_control.commit(1);\n\n\t\treturn idx;\n\t}\n\n\tvoid TimerQueryVK::end(uint32_t _idx)\n\t{\n\t\tQuery& query = m_query[_idx];\n\t\tquery.m_ready = true;\n\t\tquery.m_completed = s_renderVK->m_cmd.m_submitted + s_renderVK->m_cmd.m_numFramesInFlight;\n\n\t\tconst VkCommandBuffer commandBuffer = s_renderVK->m_commandBuffer;\n\t\tconst uint32_t offset = _idx * 2 + 0;\n\n\t\tvkCmdWriteTimestamp(commandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, m_queryPool, offset + 1);\n\n\t\tvkCmdCopyQueryPoolResults(\n\t\t\t  commandBuffer\n\t\t\t, m_queryPool\n\t\t\t, offset\n\t\t\t, 2\n\t\t\t, m_readback\n\t\t\t, offset * sizeof(uint64_t)\n\t\t\t, sizeof(uint64_t)\n\t\t\t, VK_QUERY_RESULT_WAIT_BIT | VK_QUERY_RESULT_64_BIT\n\t\t\t);\n\n\t\tsetMemoryBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT);\n\n\t\twhile (update() )\n\t\t{\n\t\t}\n\t}\n\n\tbool TimerQueryVK::update()\n\t{\n\t\tif (0 != m_control.available() )\n\t\t{\n\t\t\tuint32_t idx = m_control.m_read;\n\t\t\tQuery& query = m_query[idx];\n\n\t\t\tif (!query.m_ready)\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (query.m_completed > s_renderVK->m_cmd.m_submitted)\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tm_control.consume(1);\n\n\t\t\tResult& result = m_result[query.m_resultIdx];\n\t\t\t--result.m_pending;\n\t\t\tresult.m_frameNum = query.m_frameNum;\n\n\t\t\tuint32_t offset = idx * 2;\n\t\t\tresult.m_begin  = m_queryResult[offset+0];\n\t\t\tresult.m_end    = m_queryResult[offset+1];\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tVkResult OcclusionQueryVK::init()\n\t{\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tconst VkDevice device = s_renderVK->m_device;\n\t\tconst VkCommandBuffer commandBuffer = s_renderVK->m_commandBuffer;\n\n\t\tconst uint32_t count = BX_COUNTOF(m_handle);\n\n\t\tVkQueryPoolCreateInfo qpci;\n\t\tqpci.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;\n\t\tqpci.pNext = NULL;\n\t\tqpci.flags = 0;\n\t\tqpci.queryType = VK_QUERY_TYPE_OCCLUSION;\n\t\tqpci.queryCount = count;\n\t\tqpci.pipelineStatistics = 0;\n\n\t\tresult = vkCreateQueryPool(device, &qpci, s_renderVK->m_allocatorCb, &m_queryPool);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create occlusion query error: vkCreateQueryPool failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tvkCmdResetQueryPool(commandBuffer, m_queryPool, 0, count);\n\n\t\tconst uint32_t size = count * sizeof(uint32_t);\n\t\tresult = s_renderVK->createReadbackBuffer(size, &m_readback, &m_readbackMemory);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\treturn result;\n\t\t}\n\n\t\tresult = vkMapMemory(device, m_readbackMemory, 0, VK_WHOLE_SIZE, 0, (void**)&m_queryResult);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create occlusion query error: vkMapMemory failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tm_control.reset();\n\n\t\treturn result;\n\t}\n\n\tvoid OcclusionQueryVK::shutdown()\n\t{\n\t\tvkDestroy(m_queryPool);\n\t\tvkDestroy(m_readback);\n\t\tvkUnmapMemory(s_renderVK->m_device, m_readbackMemory);\n\t\tvkDestroy(m_readbackMemory);\n\t}\n\n\tvoid OcclusionQueryVK::begin(OcclusionQueryHandle _handle)\n\t{\n\t\tm_control.reserve(1);\n\n\t\tconst VkCommandBuffer commandBuffer = s_renderVK->m_commandBuffer;\n\n\t\tm_handle[m_control.m_current] = _handle;\n\t\tvkCmdBeginQuery(commandBuffer, m_queryPool, _handle.idx, 0);\n\t}\n\n\tvoid OcclusionQueryVK::end()\n\t{\n\t\tconst VkCommandBuffer commandBuffer = s_renderVK->m_commandBuffer;\n\n\t\tconst OcclusionQueryHandle handle = m_handle[m_control.m_current];\n\t\tvkCmdEndQuery(commandBuffer, m_queryPool, handle.idx);\n\n\t\tm_control.commit(1);\n\t}\n\n\tvoid OcclusionQueryVK::flush(Frame* _render)\n\t{\n\t\tif (0 < m_control.available() )\n\t\t{\n\t\t\tVkCommandBuffer commandBuffer = s_renderVK->m_commandBuffer;\n\n\t\t\tconst uint32_t size = m_control.m_size;\n\n\t\t\t// need to copy each result individually because VK_QUERY_RESULT_WAIT_BIT causes\n\t\t\t// vkWaitForFences to hang indefinitely if we copy all results (including unavailable ones)\n\t\t\tfor (uint32_t ii = 0, num = m_control.available(); ii < num; ++ii)\n\t\t\t{\n\t\t\t\tconst OcclusionQueryHandle& handle = m_handle[(m_control.m_read + ii) % size];\n\t\t\t\tif (isValid(handle) )\n\t\t\t\t{\n\t\t\t\t\tvkCmdCopyQueryPoolResults(\n\t\t\t\t\t\t  commandBuffer\n\t\t\t\t\t\t, m_queryPool\n\t\t\t\t\t\t, handle.idx\n\t\t\t\t\t\t, 1\n\t\t\t\t\t\t, m_readback\n\t\t\t\t\t\t, handle.idx * sizeof(uint32_t)\n\t\t\t\t\t\t, sizeof(uint32_t)\n\t\t\t\t\t\t, VK_QUERY_RESULT_WAIT_BIT\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsetMemoryBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT);\n\t\t\ts_renderVK->kick(true);\n\n\t\t\tcommandBuffer = s_renderVK->m_commandBuffer;\n\n\t\t\t// resetting in the new command buffer prevents a false positive validation layer error\n\t\t\tconst uint32_t count = BX_COUNTOF(m_handle);\n\t\t\tvkCmdResetQueryPool(commandBuffer, m_queryPool, 0, count);\n\n\t\t\tresolve(_render);\n\t\t}\n\t}\n\n\tvoid OcclusionQueryVK::resolve(Frame* _render)\n\t{\n\t\twhile (0 != m_control.available() )\n\t\t{\n\t\t\tOcclusionQueryHandle handle = m_handle[m_control.m_read];\n\t\t\tif (isValid(handle) )\n\t\t\t{\n\t\t\t\t_render->m_occlusion[handle.idx] = m_queryResult[handle.idx];\n\t\t\t}\n\t\t\tm_control.consume(1);\n\t\t}\n\t}\n\n\tvoid OcclusionQueryVK::invalidate(OcclusionQueryHandle _handle)\n\t{\n\t\tconst uint32_t size = m_control.m_size;\n\n\t\tfor (uint32_t ii = 0, num = m_control.available(); ii < num; ++ii)\n\t\t{\n\t\t\tOcclusionQueryHandle& handle = m_handle[(m_control.m_read + ii) % size];\n\t\t\tif (handle.idx == _handle.idx)\n\t\t\t{\n\t\t\t\thandle.idx = bgfx::kInvalidHandle;\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid ReadbackVK::create(VkImage _image, uint32_t _width, uint32_t _height, TextureFormat::Enum _format)\n\t{\n\t\tm_image  = _image;\n\t\tm_width  = _width;\n\t\tm_height = _height;\n\t\tm_format = _format;\n\t}\n\n\tvoid ReadbackVK::destroy()\n\t{\n\t\tm_image = VK_NULL_HANDLE;\n\t}\n\n\tuint32_t ReadbackVK::pitch(uint8_t _mip) const\n\t{\n\t\tuint32_t mipWidth = bx::uint32_max(1, m_width >> _mip);\n\t\tuint8_t bpp = bimg::getBitsPerPixel(bimg::TextureFormat::Enum(m_format) );\n\t\treturn mipWidth * bpp / 8;\n\t}\n\n\tvoid ReadbackVK::copyImageToBuffer(VkCommandBuffer _commandBuffer, VkBuffer _buffer, VkImageLayout _layout, VkImageAspectFlags _aspect, uint8_t _mip) const\n\t{\n\t\tuint32_t mipWidth  = bx::uint32_max(1, m_width  >> _mip);\n\t\tuint32_t mipHeight = bx::uint32_max(1, m_height >> _mip);\n\n\t\tsetImageMemoryBarrier(\n\t\t\t  _commandBuffer\n\t\t\t, m_image\n\t\t\t, _aspect\n\t\t\t, _layout\n\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL\n\t\t\t, _mip\n\t\t\t, 1\n\t\t\t, 0\n\t\t\t, 1\n\t\t\t);\n\n\t\tVkBufferImageCopy bic;\n\t\tbic.bufferOffset = 0;\n\t\tbic.bufferRowLength   = mipWidth;\n\t\tbic.bufferImageHeight = mipHeight;\n\t\tbic.imageSubresource.aspectMask     = _aspect;\n\t\tbic.imageSubresource.mipLevel       = _mip;\n\t\tbic.imageSubresource.baseArrayLayer = 0;\n\t\tbic.imageSubresource.layerCount     = 1;\n\t\tbic.imageOffset = { 0, 0, 0 };\n\t\tbic.imageExtent = { mipWidth, mipHeight, 1 };\n\n\t\tvkCmdCopyImageToBuffer(\n\t\t\t  _commandBuffer\n\t\t\t, m_image\n\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL\n\t\t\t, _buffer\n\t\t\t, 1\n\t\t\t, &bic\n\t\t\t);\n\n\t\t// Make changes to the buffer visible to the host\n\t\tsetMemoryBarrier(\n\t\t\t_commandBuffer\n\t\t\t, VK_PIPELINE_STAGE_TRANSFER_BIT\n\t\t\t, VK_PIPELINE_STAGE_HOST_BIT\n\t\t\t);\n\n\t\tsetImageMemoryBarrier(\n\t\t\t  _commandBuffer\n\t\t\t, m_image\n\t\t\t, _aspect\n\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL\n\t\t\t, _layout\n\t\t\t, _mip\n\t\t\t, 1\n\t\t\t, 0\n\t\t\t, 1\n\t\t\t);\n\t}\n\n\tvoid ReadbackVK::readback(VkDeviceMemory _memory, VkDeviceSize _offset, void* _data, uint8_t _mip) const\n\t{\n\t\tif (m_image == VK_NULL_HANDLE)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tuint32_t mipHeight = bx::uint32_max(1, m_height >> _mip);\n\t\tuint32_t rowPitch = pitch(_mip);\n\n\t\tuint8_t* src;\n\t\tVK_CHECK(vkMapMemory(s_renderVK->m_device, _memory, 0, VK_WHOLE_SIZE, 0, (void**)&src) );\n\t\tsrc += _offset;\n\t\tuint8_t* dst = (uint8_t*)_data;\n\n\t\tfor (uint32_t yy = 0; yy < mipHeight; ++yy)\n\t\t{\n\t\t\tbx::memCopy(dst, src, rowPitch);\n\t\t\tsrc += rowPitch;\n\t\t\tdst += rowPitch;\n\t\t}\n\n\t\tvkUnmapMemory(s_renderVK->m_device, _memory);\n\t}\n\n\tVkResult TextureVK::create(VkCommandBuffer _commandBuffer, uint32_t _width, uint32_t _height, uint64_t _flags, VkFormat _format)\n\t{\n\t\tBX_ASSERT(0 != (_flags & BGFX_TEXTURE_RT_MASK), "");\n\t\t_flags |= BGFX_TEXTURE_RT_WRITE_ONLY;\n\n\t\tm_flags     = _flags;\n\t\tm_width     = _width;\n\t\tm_height    = _height;\n\t\tm_depth     = 1;\n\t\tm_numLayers = 1;\n\t\tm_requestedFormat = uint8_t(bimg::TextureFormat::Count);\n\t\tm_textureFormat   = uint8_t(bimg::TextureFormat::Count);\n\t\tm_format = _format;\n\t\tm_components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };\n\t\tm_aspectMask = getAspectMask(m_format);\n\t\tm_sampler = s_msaa[bx::uint32_satsub( (m_flags & BGFX_TEXTURE_RT_MSAA_MASK) >> BGFX_TEXTURE_RT_MSAA_SHIFT, 1)];\n\t\tm_type = VK_IMAGE_VIEW_TYPE_2D;\n\t\tm_numMips = 1;\n\t\tm_numSides = 1;\n\n\t\tVkResult result = createImages(_commandBuffer);\n\n\t\tif (VK_SUCCESS == result)\n\t\t{\n\t\t\tconst VkImageLayout layout = 0 != (m_aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) )\n\t\t\t\t? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL\n\t\t\t\t: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL\n\t\t\t\t;\n\t\t\tsetImageMemoryBarrier(_commandBuffer, layout);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tVkResult TextureVK::createImages(VkCommandBuffer _commandBuffer)\n\t{\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tconst VkAllocationCallbacks* allocatorCb = s_renderVK->m_allocatorCb;\n\t\tconst VkDevice device = s_renderVK->m_device;\n\n\t\tif (m_sampler.Count > 1)\n\t\t{\n\t\t\tBX_ASSERT(VK_IMAGE_VIEW_TYPE_3D != m_type, "Can\'t create multisample 3D image.");\n\t\t\tBX_ASSERT(m_numMips <= 1, "Can\'t create multisample image with mip chain.");\n\t\t}\n\n\t\t// create texture and allocate its device memory\n\t\tVkImageCreateInfo ici;\n\t\tici.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;\n\t\tici.pNext = NULL;\n\t\tici.flags = 0\n\t\t\t| (VK_IMAGE_VIEW_TYPE_CUBE == m_type\n\t\t\t\t? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT\n\t\t\t\t: 0\n\t\t\t\t)\n\t\t\t| (VK_IMAGE_VIEW_TYPE_3D == m_type\n\t\t\t\t? VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR\n\t\t\t\t: 0\n\t\t\t\t)\n\t\t\t;\n\t\tici.pQueueFamilyIndices   = NULL;\n\t\tici.queueFamilyIndexCount = 0;\n\t\tici.initialLayout         = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\tici.sharingMode           = VK_SHARING_MODE_EXCLUSIVE;\n\t\tici.usage                 = 0\n\t\t\t| VK_IMAGE_USAGE_TRANSFER_SRC_BIT\n\t\t\t| VK_IMAGE_USAGE_TRANSFER_DST_BIT\n\t\t\t| VK_IMAGE_USAGE_SAMPLED_BIT\n\t\t\t| (m_flags & BGFX_TEXTURE_RT_MASK\n\t\t\t\t? (m_aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)\n\t\t\t\t\t? VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT\n\t\t\t\t\t: VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)\n\t\t\t\t: 0\n\t\t\t\t)\n\t\t\t| (m_flags & BGFX_TEXTURE_COMPUTE_WRITE ? VK_IMAGE_USAGE_STORAGE_BIT : 0)\n\t\t\t;\n\t\tici.format        = m_format;\n\t\tici.samples       = m_sampler.Sample;\n\t\tici.mipLevels     = m_numMips;\n\t\tici.arrayLayers   = m_numSides;\n\t\tici.extent.width  = m_width;\n\t\tici.extent.height = m_height;\n\t\tici.extent.depth  = m_depth;\n\t\tici.imageType     = VK_IMAGE_VIEW_TYPE_3D == m_type\n\t\t\t? VK_IMAGE_TYPE_3D\n\t\t\t: VK_IMAGE_TYPE_2D\n\t\t\t;\n\t\tici.tiling        = VK_IMAGE_TILING_OPTIMAL;\n\n\t\tresult = vkCreateImage(device, &ici, allocatorCb, &m_textureImage);\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create texture image error: vkCreateImage failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tVkMemoryRequirements imageMemReq;\n\t\tvkGetImageMemoryRequirements(device, m_textureImage, &imageMemReq);\n\n\t\tresult = s_renderVK->allocateMemory(&imageMemReq, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &m_textureDeviceMem);\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create texture image error: allocateMemory failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tresult = vkBindImageMemory(device, m_textureImage, m_textureDeviceMem, 0);\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create texture image error: vkBindImageMemory failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tm_sampledLayout = m_flags & BGFX_TEXTURE_COMPUTE_WRITE\n\t\t\t? VK_IMAGE_LAYOUT_GENERAL\n\t\t\t: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL\n\t\t\t;\n\n\t\tconst bool needResolve = true\n\t\t\t&& 1 < m_sampler.Count\n\t\t\t&& 0 != (ici.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)\n\t\t\t&& 0 == (m_flags & BGFX_TEXTURE_MSAA_SAMPLE)\n\t\t\t&& 0 == (m_flags & BGFX_TEXTURE_RT_WRITE_ONLY)\n\t\t\t;\n\n\t\tif (needResolve)\n\t\t{\n\t\t\tVkImageCreateInfo ici_resolve = ici;\n\t\t\tici_resolve.samples = s_msaa[0].Sample;\n\t\t\tici_resolve.usage &= ~VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;\n\t\t\tici_resolve.flags &= ~VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;\n\n\t\t\tresult = vkCreateImage(device, &ici_resolve, allocatorCb, &m_singleMsaaImage);\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create texture image error: vkCreateImage failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tVkMemoryRequirements imageMemReq_resolve;\n\t\t\tvkGetImageMemoryRequirements(device, m_singleMsaaImage, &imageMemReq_resolve);\n\n\t\t\tresult = s_renderVK->allocateMemory(&imageMemReq_resolve, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &m_singleMsaaDeviceMem);\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create texture image error: allocateMemory failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tresult = vkBindImageMemory(device, m_singleMsaaImage, m_singleMsaaDeviceMem, 0);\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create texture image error: vkBindImageMemory failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tsetImageMemoryBarrier(_commandBuffer, m_sampledLayout, true);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tvoid* TextureVK::create(VkCommandBuffer _commandBuffer, const Memory* _mem, uint64_t _flags, uint8_t _skip)\n\t{\n\t\tbimg::ImageContainer imageContainer;\n\n\t\tif (bimg::imageParse(imageContainer, _mem->data, _mem->size) )\n\t\t{\n\t\t\tconst bimg::ImageBlockInfo& blockInfo = bimg::getBlockInfo(imageContainer.m_format);\n\t\t\tconst uint8_t startLod = bx::min<uint8_t>(_skip, imageContainer.m_numMips - 1);\n\n\t\t\tbimg::TextureInfo ti;\n\t\t\tbimg::imageGetSize(\n\t\t\t\t  &ti\n\t\t\t\t, uint16_t(imageContainer.m_width  >> startLod)\n\t\t\t\t, uint16_t(imageContainer.m_height >> startLod)\n\t\t\t\t, uint16_t(imageContainer.m_depth  >> startLod)\n\t\t\t\t, imageContainer.m_cubeMap\n\t\t\t\t, 1 < imageContainer.m_numMips\n\t\t\t\t, imageContainer.m_numLayers\n\t\t\t\t, imageContainer.m_format\n\t\t\t\t);\n\n\t\t\tti.numMips = bx::min<uint8_t>(imageContainer.m_numMips - startLod, ti.numMips);\n\n\t\t\tm_flags     = _flags;\n\t\t\tm_width     = ti.width;\n\t\t\tm_height    = ti.height;\n\t\t\tm_depth     = ti.depth;\n\t\t\tm_numLayers = ti.numLayers;\n\t\t\tm_requestedFormat = uint8_t(imageContainer.m_format);\n\t\t\tm_textureFormat   = uint8_t(getViableTextureFormat(imageContainer) );\n\t\t\tm_format = bimg::isDepth(bimg::TextureFormat::Enum(m_textureFormat) )\n\t\t\t\t? s_textureFormat[m_textureFormat].m_fmtDsv\n\t\t\t\t: (m_flags & BGFX_TEXTURE_SRGB) ? s_textureFormat[m_textureFormat].m_fmtSrgb : s_textureFormat[m_textureFormat].m_fmt\n\t\t\t\t;\n\t\t\tm_components = s_textureFormat[m_textureFormat].m_mapping;\n\n\t\t\tconst bool convert = m_textureFormat != m_requestedFormat;\n\t\t\tconst uint8_t bpp = bimg::getBitsPerPixel(bimg::TextureFormat::Enum(m_textureFormat) );\n\n\t\t\tm_aspectMask = getAspectMask(m_format);\n\t\t\tm_sampler = s_msaa[bx::uint32_satsub( (m_flags & BGFX_TEXTURE_RT_MSAA_MASK) >> BGFX_TEXTURE_RT_MSAA_SHIFT, 1)];\n\n\t\t\tif (imageContainer.m_cubeMap)\n\t\t\t{\n\t\t\t\tm_type = imageContainer.m_numLayers > 1\n\t\t\t\t\t? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY\n\t\t\t\t\t: VK_IMAGE_VIEW_TYPE_CUBE\n\t\t\t\t\t;\n\t\t\t}\n\t\t\telse if (imageContainer.m_depth > 1)\n\t\t\t{\n\t\t\t\tm_type = VK_IMAGE_VIEW_TYPE_3D;\n\t\t\t}\n\t\t\telse if (imageContainer.m_numLayers > 1)\n\t\t\t{\n\t\t\t\tm_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tm_type = VK_IMAGE_VIEW_TYPE_2D;\n\t\t\t}\n\n\t\t\tm_numMips = ti.numMips;\n\t\t\tm_numSides = ti.numLayers * (imageContainer.m_cubeMap ? 6 : 1);\n\t\t\tconst uint16_t numSides = ti.numLayers * (imageContainer.m_cubeMap ? 6 : 1);\n\t\t\tconst uint32_t numSrd = numSides * ti.numMips;\n\n\t\t\tuint32_t kk = 0;\n\n\t\t\tconst bool compressed = bimg::isCompressed(bimg::TextureFormat::Enum(m_textureFormat) );\n\t\t\tconst bool swizzle = TextureFormat::BGRA8 == m_textureFormat && 0 != (m_flags & BGFX_TEXTURE_COMPUTE_WRITE);\n\n\t\t\tconst bool writeOnly    = 0 != (m_flags & BGFX_TEXTURE_RT_WRITE_ONLY);\n\t\t\tconst bool computeWrite = 0 != (m_flags & BGFX_TEXTURE_COMPUTE_WRITE);\n\t\t\tconst bool renderTarget = 0 != (m_flags & BGFX_TEXTURE_RT_MASK);\n\t\t\tconst bool blit         = 0 != (m_flags & BGFX_TEXTURE_BLIT_DST);\n\n\t\t\tBX_UNUSED(swizzle, writeOnly, computeWrite, renderTarget, blit);\n\n\t\t\tBX_TRACE(\n\t\t\t\t  "Texture %3d: %s (requested: %s), %dx%dx%d%s RT[%c], BO[%c], CW[%c]%s."\n\t\t\t\t, (int)(this - s_renderVK->m_textures)\n\t\t\t\t, getName( (TextureFormat::Enum)m_textureFormat)\n\t\t\t\t, getName( (TextureFormat::Enum)m_requestedFormat)\n\t\t\t\t, ti.width\n\t\t\t\t, ti.height\n\t\t\t\t, ti.depth\n\t\t\t\t, imageContainer.m_cubeMap ? "x6" : ""\n\t\t\t\t, renderTarget ? \'x\' : \' \'\n\t\t\t\t, writeOnly ? \'x\' : \' \'\n\t\t\t\t, computeWrite ? \'x\' : \' \'\n\t\t\t\t, swizzle ? " (swizzle BGRA8 -> RGBA8)" : ""\n\t\t\t\t);\n\n\t\t\tVK_CHECK(createImages(_commandBuffer) );\n\n\t\t\t// decode images\n\t\t\tstruct ImageInfo\n\t\t\t{\n\t\t\t\tuint8_t* data;\n\t\t\t\tuint32_t width;\n\t\t\t\tuint32_t height;\n\t\t\t\tuint32_t depth;\n\t\t\t\tuint32_t pitch;\n\t\t\t\tuint32_t slice;\n\t\t\t\tuint32_t size;\n\t\t\t\tuint8_t  mipLevel;\n\t\t\t\tuint8_t  layer;\n\t\t\t};\n\n\t\t\tImageInfo* imageInfos = (ImageInfo*)BX_ALLOC(g_allocator, sizeof(ImageInfo) * numSrd);\n\t\t\tbx::memSet(imageInfos, 0, sizeof(ImageInfo) * numSrd);\n\t\t\tuint32_t alignment = 1; // tightly aligned buffer\n\n\t\t\tfor (uint8_t side = 0; side < numSides; ++side)\n\t\t\t{\n\t\t\t\tfor (uint8_t lod = 0; lod < ti.numMips; ++lod)\n\t\t\t\t{\n\t\t\t\t\tbimg::ImageMip mip;\n\n\t\t\t\t\tif (bimg::imageGetRawData(imageContainer, side, lod + startLod, _mem->data, _mem->size, mip) )\n\t\t\t\t\t{\n\t\t\t\t\t\tif (convert)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst uint32_t pitch = bx::strideAlign(bx::max<uint32_t>(mip.m_width, 4) * bpp / 8, alignment);\n\t\t\t\t\t\t\tconst uint32_t slice = bx::strideAlign(bx::max<uint32_t>(mip.m_height, 4) * pitch, alignment);\n\t\t\t\t\t\t\tconst uint32_t size  = slice * mip.m_depth;\n\n\t\t\t\t\t\t\tuint8_t* temp = (uint8_t*)BX_ALLOC(g_allocator, size);\n\t\t\t\t\t\t\tbimg::imageDecodeToBgra8(\n\t\t\t\t\t\t\t\t  g_allocator\n\t\t\t\t\t\t\t\t, temp\n\t\t\t\t\t\t\t\t, mip.m_data\n\t\t\t\t\t\t\t\t, mip.m_width\n\t\t\t\t\t\t\t\t, mip.m_height\n\t\t\t\t\t\t\t\t, pitch\n\t\t\t\t\t\t\t\t, mip.m_format\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\timageInfos[kk].data     = temp;\n\t\t\t\t\t\t\timageInfos[kk].width    = mip.m_width;\n\t\t\t\t\t\t\timageInfos[kk].height   = mip.m_height;\n\t\t\t\t\t\t\timageInfos[kk].depth    = mip.m_depth;\n\t\t\t\t\t\t\timageInfos[kk].pitch    = pitch;\n\t\t\t\t\t\t\timageInfos[kk].slice    = slice;\n\t\t\t\t\t\t\timageInfos[kk].size     = size;\n\t\t\t\t\t\t\timageInfos[kk].mipLevel = lod;\n\t\t\t\t\t\t\timageInfos[kk].layer    = side;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (compressed)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst uint32_t pitch = bx::strideAlign( (mip.m_width / blockInfo.blockWidth) * mip.m_blockSize, alignment);\n\t\t\t\t\t\t\tconst uint32_t slice = bx::strideAlign( (mip.m_height / blockInfo.blockHeight) * pitch, alignment);\n\t\t\t\t\t\t\tconst uint32_t size  = slice * mip.m_depth;\n\n\t\t\t\t\t\t\tuint8_t* temp = (uint8_t*)BX_ALLOC(g_allocator, size);\n\t\t\t\t\t\t\tbimg::imageCopy(\n\t\t\t\t\t\t\t\t  temp\n\t\t\t\t\t\t\t\t, mip.m_height / blockInfo.blockHeight\n\t\t\t\t\t\t\t\t, (mip.m_width / blockInfo.blockWidth) * mip.m_blockSize\n\t\t\t\t\t\t\t\t, mip.m_depth\n\t\t\t\t\t\t\t\t, mip.m_data\n\t\t\t\t\t\t\t\t, pitch\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\timageInfos[kk].data     = temp;\n\t\t\t\t\t\t\timageInfos[kk].width    = mip.m_width;\n\t\t\t\t\t\t\timageInfos[kk].height   = mip.m_height;\n\t\t\t\t\t\t\timageInfos[kk].depth    = mip.m_depth;\n\t\t\t\t\t\t\timageInfos[kk].pitch    = pitch;\n\t\t\t\t\t\t\timageInfos[kk].slice    = slice;\n\t\t\t\t\t\t\timageInfos[kk].size     = size;\n\t\t\t\t\t\t\timageInfos[kk].mipLevel = lod;\n\t\t\t\t\t\t\timageInfos[kk].layer    = side;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst uint32_t pitch = bx::strideAlign(mip.m_width * mip.m_bpp / 8, alignment);\n\t\t\t\t\t\t\tconst uint32_t slice = bx::strideAlign(mip.m_height * pitch, alignment);\n\t\t\t\t\t\t\tconst uint32_t size  = slice * mip.m_depth;\n\n\t\t\t\t\t\t\tuint8_t* temp = (uint8_t*)BX_ALLOC(g_allocator, size);\n\t\t\t\t\t\t\tbimg::imageCopy(\n\t\t\t\t\t\t\t\t  temp\n\t\t\t\t\t\t\t\t, mip.m_height\n\t\t\t\t\t\t\t\t, mip.m_width * mip.m_bpp / 8\n\t\t\t\t\t\t\t\t, mip.m_depth\n\t\t\t\t\t\t\t\t, mip.m_data\n\t\t\t\t\t\t\t\t, pitch\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\timageInfos[kk].data     = temp;\n\t\t\t\t\t\t\timageInfos[kk].width    = mip.m_width;\n\t\t\t\t\t\t\timageInfos[kk].height   = mip.m_height;\n\t\t\t\t\t\t\timageInfos[kk].depth    = mip.m_depth;\n\t\t\t\t\t\t\timageInfos[kk].pitch    = pitch;\n\t\t\t\t\t\t\timageInfos[kk].slice    = slice;\n\t\t\t\t\t\t\timageInfos[kk].size     = size;\n\t\t\t\t\t\t\timageInfos[kk].mipLevel = lod;\n\t\t\t\t\t\t\timageInfos[kk].layer    = side;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t++kk;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tuint32_t totalMemSize = 0;\n\t\t\tVkBufferImageCopy* bufferCopyInfo = (VkBufferImageCopy*)BX_ALLOC(g_allocator, sizeof(VkBufferImageCopy) * numSrd);\n\n\t\t\tfor (uint32_t ii = 0; ii < numSrd; ++ii)\n\t\t\t{\n\t\t\t\tconst uint32_t idealWidth  = bx::max<uint32_t>(1, m_width  >> imageInfos[ii].mipLevel);\n\t\t\t\tconst uint32_t idealHeight = bx::max<uint32_t>(1, m_height >> imageInfos[ii].mipLevel);\n\t\t\t\tbufferCopyInfo[ii].bufferOffset      = totalMemSize;\n\t\t\t\tbufferCopyInfo[ii].bufferRowLength   = 0; // assume that image data are tightly aligned\n\t\t\t\tbufferCopyInfo[ii].bufferImageHeight = 0; // assume that image data are tightly aligned\n\t\t\t\tbufferCopyInfo[ii].imageSubresource.aspectMask     = m_aspectMask;\n\t\t\t\tbufferCopyInfo[ii].imageSubresource.mipLevel       = imageInfos[ii].mipLevel;\n\t\t\t\tbufferCopyInfo[ii].imageSubresource.baseArrayLayer = imageInfos[ii].layer;\n\t\t\t\tbufferCopyInfo[ii].imageSubresource.layerCount     = 1;\n\t\t\t\tbufferCopyInfo[ii].imageOffset = { 0, 0, 0 };\n\t\t\t\tbufferCopyInfo[ii].imageExtent = { idealWidth, idealHeight, imageInfos[ii].depth };\n\t\t\t\ttotalMemSize += imageInfos[ii].size;\n\t\t\t}\n\n\t\t\tif (totalMemSize > 0)\n\t\t\t{\n\t\t\t\tconst VkDevice device = s_renderVK->m_device;\n\n\t\t\t\tVkBuffer stagingBuffer;\n\t\t\t\tVkDeviceMemory stagingDeviceMem;\n\t\t\t\tVK_CHECK(s_renderVK->createStagingBuffer(totalMemSize, &stagingBuffer, &stagingDeviceMem) );\n\n\t\t\t\tuint8_t* mappedMemory;\n\t\t\t\tVK_CHECK(vkMapMemory(\n\t\t\t\t\t  device\n\t\t\t\t\t, stagingDeviceMem\n\t\t\t\t\t, 0\n\t\t\t\t\t, totalMemSize\n\t\t\t\t\t, 0\n\t\t\t\t\t, (void**)&mappedMemory\n\t\t\t\t\t) );\n\n\t\t\t\t// copy image to staging buffer\n\t\t\t\tfor (uint32_t ii = 0; ii < numSrd; ++ii)\n\t\t\t\t{\n\t\t\t\t\tbx::memCopy(mappedMemory, imageInfos[ii].data, imageInfos[ii].size);\n\t\t\t\t\tmappedMemory += imageInfos[ii].size;\n\t\t\t\t}\n\n\t\t\t\tvkUnmapMemory(device, stagingDeviceMem);\n\n\t\t\t\tcopyBufferToTexture(_commandBuffer, stagingBuffer, numSrd, bufferCopyInfo);\n\n\t\t\t\ts_renderVK->release(stagingBuffer);\n\t\t\t\ts_renderVK->release(stagingDeviceMem);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsetImageMemoryBarrier(_commandBuffer, m_sampledLayout);\n\t\t\t}\n\n\t\t\tBX_FREE(g_allocator, bufferCopyInfo);\n\n\t\t\tfor (uint32_t ii = 0; ii < numSrd; ++ii)\n\t\t\t{\n\t\t\t\tBX_FREE(g_allocator, imageInfos[ii].data);\n\t\t\t}\n\n\t\t\tBX_FREE(g_allocator, imageInfos);\n\n\t\t\tm_readback.create(m_textureImage, m_width, m_height, TextureFormat::Enum(m_textureFormat) );\n\t\t}\n\n\n\t\t\t\t\t\t\toffset = scratchBuffer.m_pos;\n\n\t\t\t\t\t\t\tm_vsChanges = 0;\n\t\t\t\t\t\t\tm_fsChanges = 0;\n\n\t\t\t\t\t\t\tbx::memCopy(&scratchBuffer.m_data[scratchBuffer.m_pos], m_vsScratch, program.m_vsh->m_size);\n\n\t\t\t\t\t\t\tscratchBuffer.m_pos += vsize;\n\n\t\tif (VK_NULL_HANDLE != m_textureImage)\n\t\t{\n\t\t\ts_renderVK->release(m_textureImage);\n\t\t\ts_renderVK->release(m_textureDeviceMem);\n\t\t}\n\n\t\tif (VK_NULL_HANDLE != m_singleMsaaImage)\n\t\t{\n\t\t\ts_renderVK->release(m_singleMsaaImage);\n\t\t\ts_renderVK->release(m_singleMsaaDeviceMem);\n\t\t}\n\n\t\tm_currentImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\tm_currentSingleMsaaImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t}\n\n\tvoid TextureVK::update(VkCommandBuffer _commandBuffer, uint8_t _side, uint8_t _mip, const Rect& _rect, uint16_t _z, uint16_t _depth, uint16_t _pitch, const Memory* _mem)\n\t{\n\t\tconst uint32_t bpp = bimg::getBitsPerPixel(bimg::TextureFormat::Enum(m_textureFormat) );\n\t\tuint32_t rectpitch = _rect.m_width * bpp / 8;\n\t\tuint32_t slicepitch = rectpitch * _rect.m_height;\n\t\tif (bimg::isCompressed(bimg::TextureFormat::Enum(m_textureFormat) ) )\n\t\t{\n\t\t\tconst bimg::ImageBlockInfo& blockInfo = bimg::getBlockInfo(bimg::TextureFormat::Enum(m_textureFormat) );\n\t\t\trectpitch  = (_rect.m_width  / blockInfo.blockWidth ) * blockInfo.blockSize;\n\t\t\tslicepitch = (_rect.m_height / blockInfo.blockHeight) * rectpitch;\n\t\t}\n\t\tconst uint32_t srcpitch = UINT16_MAX == _pitch ? rectpitch : _pitch;\n\t\tconst uint32_t size     = UINT16_MAX == _pitch ? slicepitch  * _depth: _rect.m_height * _pitch * _depth;\n\t\tconst bool convert = m_textureFormat != m_requestedFormat;\n\n\t\tVkBufferImageCopy region;\n\t\tregion.bufferOffset      = 0;\n\t\tregion.bufferRowLength   = (_pitch == UINT16_MAX ? 0 : _pitch * 8 / bpp);\n\t\tregion.bufferImageHeight = 0;\n\t\tregion.imageSubresource.aspectMask     = m_aspectMask;\n\t\tregion.imageSubresource.mipLevel       = _mip;\n\t\tregion.imageSubresource.baseArrayLayer = 0;\n\t\tregion.imageSubresource.layerCount     = 1;\n\t\tregion.imageOffset = { _rect.m_x,     _rect.m_y,      0      };\n\t\tregion.imageExtent = { _rect.m_width, _rect.m_height, _depth };\n\n\t\tuint8_t* data = _mem->data;\n\t\tuint8_t* temp = NULL;\n\n\t\tif (convert)\n\t\t{\n\t\t\ttemp = (uint8_t*)BX_ALLOC(g_allocator, slicepitch);\n\t\t\tbimg::imageDecodeToBgra8(g_allocator, temp, data, _rect.m_width, _rect.m_height, srcpitch, bimg::TextureFormat::Enum(m_requestedFormat));\n\t\t\tdata = temp;\n\n\t\t\tregion.imageExtent =\n\t\t\t{\n\t\t\t\tbx::max(1u, m_width  >> _mip),\n\t\t\t\tbx::max(1u, m_height >> _mip),\n\t\t\t\t_depth,\n\t\t\t};\n\t\t}\n\n\t\tVkBuffer stagingBuffer = VK_NULL_HANDLE;\n\t\tVkDeviceMemory stagingDeviceMem = VK_NULL_HANDLE;\n\t\tVK_CHECK(s_renderVK->createStagingBuffer(size, &stagingBuffer, &stagingDeviceMem, data) );\n\n\t\tif (VK_IMAGE_VIEW_TYPE_3D == m_type)\n\t\t{\n\t\t\tregion.imageOffset.z = _z;\n\t\t}\n\t\telse if (VK_IMAGE_VIEW_TYPE_CUBE == m_type\n\t\t||       VK_IMAGE_VIEW_TYPE_CUBE_ARRAY == m_type)\n\t\t{\n\t\t\tregion.imageSubresource.baseArrayLayer = _z * 6 + _side;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tregion.imageSubresource.baseArrayLayer = _z;\n\t\t}\n\n\t\tcopyBufferToTexture(_commandBuffer, stagingBuffer, 1, &region);\n\n\t\ts_renderVK->release(stagingBuffer);\n\t\ts_renderVK->release(stagingDeviceMem);\n\n\t\tif (NULL != temp)\n\t\t{\n\t\t\tBX_FREE(g_allocator, temp);\n\t\t}\n\t}\n\n\tvoid TextureVK::resolve(VkCommandBuffer _commandBuffer, uint8_t _resolve, uint32_t _layer, uint32_t _numLayers, uint32_t _mip)\n\t{\n\t\tconst bool needResolve = VK_NULL_HANDLE != m_singleMsaaImage;\n\n\t\tconst bool needMipGen = true\n\t\t\t&& !needResolve\n\t\t\t&& 0 != (m_flags & BGFX_TEXTURE_RT_MASK)\n\t\t\t&& 0 == (m_flags & BGFX_TEXTURE_RT_WRITE_ONLY)\n\t\t\t&& (_mip + 1) < m_numMips\n\t\t\t&& 0 != (_resolve & BGFX_RESOLVE_AUTO_GEN_MIPS);\n\n\t\tconst VkImageLayout oldLayout = m_currentImageLayout;\n\t\tconst VkImageLayout oldSingleMsaaLayout = m_currentSingleMsaaImageLayout;\n\n\t\tif (needResolve)\n\t\t{\n\t\t\tsetImageMemoryBarrier(_commandBuffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);\n\t\t\tsetImageMemoryBarrier(_commandBuffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true);\n\n\t\t\tVkImageResolve resolve;\n\t\t\tresolve.srcOffset.x = 0;\n\t\t\tresolve.srcOffset.y = 0;\n\t\t\tresolve.srcOffset.z = 0;\n\t\t\tresolve.dstOffset.x = 0;\n\t\t\tresolve.dstOffset.y = 0;\n\t\t\tresolve.dstOffset.z = 0;\n\t\t\tresolve.srcSubresource.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t\tresolve.srcSubresource.mipLevel       = _mip;\n\t\t\tresolve.srcSubresource.baseArrayLayer = _layer;\n\t\t\tresolve.srcSubresource.layerCount     = _numLayers;\n\t\t\tresolve.dstSubresource.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t\tresolve.dstSubresource.mipLevel       = _mip;\n\t\t\tresolve.dstSubresource.baseArrayLayer = _layer;\n\t\t\tresolve.dstSubresource.layerCount     = _numLayers;\n\t\t\tresolve.extent.width  = m_width;\n\t\t\tresolve.extent.height = m_height;\n\t\t\tresolve.extent.depth  = 1;\n\n\t\t\tvkCmdResolveImage(\n\t\t\t\t  _commandBuffer\n\t\t\t\t, m_textureImage\n\t\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL\n\t\t\t\t, m_singleMsaaImage\n\t\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL\n\t\t\t\t, 1\n\t\t\t\t, &resolve\n\t\t\t\t);\n\t\t}\n\n\t\tif (needMipGen)\n\t\t{\n\t\t\tsetImageMemoryBarrier(_commandBuffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n\n\t\t\tint32_t mipWidth  = bx::max<int32_t>(int32_t(m_width)  >> _mip, 1);\n\t\t\tint32_t mipHeight = bx::max<int32_t>(int32_t(m_height) >> _mip, 1);\n\n\t\t\tconst VkFilter filter = bimg::isDepth(bimg::TextureFormat::Enum(m_textureFormat) )\n\t\t\t\t? VK_FILTER_NEAREST\n\t\t\t\t: VK_FILTER_LINEAR\n\t\t\t\t;\n\n\t\t\tVkImageBlit blit;\n\t\t\tblit.srcOffsets[0] = { 0, 0, 0 };\n\t\t\tblit.srcOffsets[1] = { mipWidth, mipHeight, 1 };\n\t\t\tblit.srcSubresource.aspectMask = m_aspectMask;\n\t\t\tblit.srcSubresource.mipLevel = 0;\n\t\t\tblit.srcSubresource.baseArrayLayer = _layer;\n\t\t\tblit.srcSubresource.layerCount = _numLayers;\n\t\t\tblit.dstOffsets[0] = { 0, 0, 0 };\n\t\t\tblit.dstOffsets[1] = { mipWidth, mipHeight, 1 };\n\t\t\tblit.dstSubresource.aspectMask = m_aspectMask;\n\t\t\tblit.dstSubresource.mipLevel = 0;\n\t\t\tblit.dstSubresource.baseArrayLayer = _layer;\n\t\t\tblit.dstSubresource.layerCount = _numLayers;\n\n\t\t\tfor (uint32_t i = _mip + 1; i < m_numMips; i++)\n\t\t\t{\n\t\t\t\tblit.srcOffsets[1] = { mipWidth, mipHeight, 1 };\n\t\t\t\tblit.srcSubresource.mipLevel = i - 1;\n\n\t\t\t\tmipWidth  = bx::uint32_max(mipWidth  >> 1, 1);\n\t\t\t\tmipHeight = bx::uint32_max(mipHeight >> 1, 1);\n\n\t\t\t\tblit.dstOffsets[1] = { mipWidth, mipHeight, 1 };\n\t\t\t\tblit.dstSubresource.mipLevel = i;\n\n\t\t\t\tvk::setImageMemoryBarrier(\n\t\t\t\t\t  _commandBuffer\n\t\t\t\t\t, m_textureImage\n\t\t\t\t\t, m_aspectMask\n\t\t\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL\n\t\t\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL\n\t\t\t\t\t, blit.srcSubresource.mipLevel\n\t\t\t\t\t, 1\n\t\t\t\t\t, _layer\n\t\t\t\t\t, _numLayers\n\t\t\t\t\t);\n\n\t\t\t\tvkCmdBlitImage(\n\t\t\t\t\t  _commandBuffer\n\t\t\t\t\t, m_textureImage\n\t\t\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL\n\t\t\t\t\t, m_textureImage\n\t\t\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL\n\t\t\t\t\t, 1\n\t\t\t\t\t, &blit\n\t\t\t\t\t, filter\n\t\t\t\t\t);\n\t\t\t}\n\n\t\t\tvk::setImageMemoryBarrier(\n\t\t\t\t  _commandBuffer\n\t\t\t\t, m_textureImage\n\t\t\t\t, m_aspectMask\n\t\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL\n\t\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL\n\t\t\t\t, _mip\n\t\t\t\t, m_numMips - _mip - 1\n\t\t\t\t, _layer\n\t\t\t\t, _numLayers\n\t\t\t\t);\n\t\t}\n\n\t\tsetImageMemoryBarrier(_commandBuffer, oldLayout);\n\t\tsetImageMemoryBarrier(_commandBuffer, oldSingleMsaaLayout, true);\n\t}\n\n\tvoid TextureVK::copyBufferToTexture(VkCommandBuffer _commandBuffer, VkBuffer _stagingBuffer, uint32_t _bufferImageCopyCount, VkBufferImageCopy* _bufferImageCopy)\n\t{\n\t\tconst VkImageLayout oldLayout = m_currentImageLayout == VK_IMAGE_LAYOUT_UNDEFINED\n\t\t\t? m_sampledLayout\n\t\t\t: m_currentImageLayout\n\t\t\t;\n\n\t\tsetImageMemoryBarrier(_commandBuffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n\n\t\tvkCmdCopyBufferToImage(\n\t\t\t  _commandBuffer\n\t\t\t, _stagingBuffer\n\t\t\t, m_textureImage\n\t\t\t, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL\n\t\t\t, _bufferImageCopyCount\n\t\t\t, _bufferImageCopy\n\t\t\t);\n\n\t\tsetImageMemoryBarrier(_commandBuffer, oldLayout);\n\t}\n\n\tVkImageLayout TextureVK::setImageMemoryBarrier(VkCommandBuffer _commandBuffer, VkImageLayout _newImageLayout, bool _singleMsaaImage)\n\t{\n\t\tif (_singleMsaaImage && VK_NULL_HANDLE == m_singleMsaaImage)\n\t\t{\n\t\t\treturn VK_IMAGE_LAYOUT_UNDEFINED;\n\t\t}\n\n\t\tVkImageLayout& currentLayout = _singleMsaaImage\n\t\t\t? m_currentSingleMsaaImageLayout\n\t\t\t: m_currentImageLayout\n\t\t\t;\n\n\t\tconst VkImageLayout oldLayout = currentLayout;\n\n\t\tif (currentLayout == _newImageLayout)\n\t\t{\n\t\t\treturn oldLayout;\n\t\t}\n\n\t\tconst VkImage image = _singleMsaaImage\n\t\t\t? m_singleMsaaImage\n\t\t\t: m_textureImage\n\t\t\t;\n\n\t\tvk::setImageMemoryBarrier(\n\t\t\t  _commandBuffer\n\t\t\t, image\n\t\t\t, m_aspectMask\n\t\t\t, currentLayout\n\t\t\t, _newImageLayout\n\t\t\t);\n\n\t\tcurrentLayout = _newImageLayout;\n\t\treturn oldLayout;\n\t}\n\n\tVkResult TextureVK::createView(uint32_t _layer, uint32_t _numLayers, uint32_t _mip, uint32_t _numMips, VkImageViewType _type, VkImageAspectFlags _aspectMask, bool _renderTarget, ::VkImageView* _view) const\n\t{\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tif (VK_IMAGE_VIEW_TYPE_3D == m_type)\n\t\t{\n\t\t\tBX_ASSERT(false\n\t\t\t\t  || !_renderTarget\n\t\t\t\t  || !(m_aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) )\n\t\t\t\t, "3D image can\'t be a depth attachment"\n\t\t\t);\n\t\t}\n\n\t\tif (VK_IMAGE_VIEW_TYPE_CUBE       == _type\n\t\t||  VK_IMAGE_VIEW_TYPE_CUBE_ARRAY == _type)\n\t\t{\n\t\t\tBX_ASSERT(_numLayers % 6 == 0, "");\n\t\t\tBX_ASSERT(\n\t\t\t\t  VK_IMAGE_VIEW_TYPE_3D != m_type\n\t\t\t\t, "3D image can\'t be aliased as a cube texture"\n\t\t\t\t);\n\t\t}\n\n\t\tVkImageViewCreateInfo viewInfo;\n\t\tviewInfo.sType      = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n\t\tviewInfo.pNext      = NULL;\n\t\tviewInfo.flags      = 0;\n\t\tviewInfo.image      = ((VK_NULL_HANDLE != m_singleMsaaImage) && !_renderTarget)\n\t\t\t? m_singleMsaaImage\n\t\t\t: m_textureImage\n\t\t\t\t\t\t\t\tbx::memCopy(&scratchBuffer.m_data[scratchBuffer.m_pos + vsize], m_fsScratch, program.m_fsh->m_size);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tm_vsChanges = 0;\n\t\t\t\t\t\t\tm_fsChanges = 0;\n\t\t\t\t\t\t\tscratchBuffer.m_pos += total;\n\t\t\t\t\t\t}\n\n\t\tviewInfo.subresourceRange.levelCount     = _numMips;\n\t\tviewInfo.subresourceRange.baseArrayLayer = _layer;\n\t\tviewInfo.subresourceRange.layerCount     = 1;\n\n\t\tif (VK_IMAGE_VIEW_TYPE_2D != _type\n\t\t&&  VK_IMAGE_VIEW_TYPE_3D != _type)\n\t\t{\n\t\t\tviewInfo.subresourceRange.layerCount = VK_IMAGE_VIEW_TYPE_CUBE == _type\n\t\t\t\t? 6\n\t\t\t\t: _numLayers\n\t\t\t\t;\n\t\t}\n\n\t\tVkImageView view = VK_NULL_HANDLE;\n\n\t\tresult = vkCreateImageView(\n\t\t\t  s_renderVK->m_device\n\t\t\t, &viewInfo\n\t\t\t, s_renderVK->m_allocatorCb\n\t\t\t, &view\n\t\t\t);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create texture view error: vkCreateImageView failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\t*_view = view;\n\n\t\treturn result;\n\t}\n\n\tVkImageAspectFlags TextureVK::getAspectMask(VkFormat _format)\n\t{\n\t\tswitch (_format)\n\t\t{\n\t\tcase VK_FORMAT_S8_UINT:\n\t\t\treturn VK_IMAGE_ASPECT_STENCIL_BIT;\n\t\t\tbreak;\n\t\tcase VK_FORMAT_D16_UNORM:\n\t\tcase VK_FORMAT_X8_D24_UNORM_PACK32:\n\t\tcase VK_FORMAT_D32_SFLOAT:\n\t\t\treturn VK_IMAGE_ASPECT_DEPTH_BIT;\n\t\tcase VK_FORMAT_D16_UNORM_S8_UINT:\n\t\tcase VK_FORMAT_D24_UNORM_S8_UINT:\n\t\tcase VK_FORMAT_D32_SFLOAT_S8_UINT:\n\t\t\treturn VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;\n\t\tdefault:\n\t\t\treturn VK_IMAGE_ASPECT_COLOR_BIT;\n\t\t}\n\t}\n\n\tVkResult SwapChainVK::create(VkCommandBuffer _commandBuffer, void* _nwh, const Resolution& _resolution, TextureFormat::Enum _depthFormat)\n\t{\n\t\tstruct ErrorState\n\t\t{\n\t\t\tenum Enum\n\t\t\t{\n\t\t\t\tDefault,\n\t\t\t\tSurfaceCreated,\n\t\t\t\tSwapChainCreated,\n\t\t\t\tAttachmentsCreated\n\t\t\t};\n\t\t};\n\n\t\tErrorState::Enum errorState = ErrorState::Default;\n\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tif (NULL == _nwh)\n\t\t{\n\t\t\treturn result;\n\t\t}\n\n\t\tm_nwh = _nwh;\n\t\tm_resolution = _resolution;\n\t\tm_depthFormat = TextureFormat::Count == _depthFormat ? TextureFormat::D24S8 : _depthFormat;\n\n\t\tm_queue = s_renderVK->m_globalQueue;\n\n\t\tresult = createSurface();\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create swap chain error: creating surface failed %d: %s.", result, getName(result) );\n\t\t\tgoto error;\n\t\t}\n\n\t\terrorState = ErrorState::SurfaceCreated;\n\n\t\t{\n\t\t\tm_sci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;\n\t\t\tm_sci.pNext = NULL;\n\t\t\tm_sci.flags = 0;\n\t\t\tm_sci.imageArrayLayers      = 1;\n\t\t\tm_sci.imageSharingMode      = VK_SHARING_MODE_EXCLUSIVE;\n\t\t\tm_sci.queueFamilyIndexCount = 0;\n\t\t\tm_sci.pQueueFamilyIndices   = NULL;\n\t\t\tm_sci.preTransform          = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;\n\t\t\tm_sci.oldSwapchain          = VK_NULL_HANDLE;\n\n\t\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_backBufferColorImageView); ++ii)\n\t\t\t{\n\t\t\t\tm_backBufferColorImage[ii]     = VK_NULL_HANDLE;\n\t\t\t\tm_backBufferColorImageView[ii] = VK_NULL_HANDLE;\n\t\t\t\tm_backBufferFrameBuffer[ii]    = VK_NULL_HANDLE;\n\t\t\t\tm_backBufferFence[ii]          = VK_NULL_HANDLE;\n\t\t\t\tm_presentDoneSemaphore[ii]     = VK_NULL_HANDLE;\n\t\t\t\tm_renderDoneSemaphore[ii]      = VK_NULL_HANDLE;\n\t\t\t}\n\n\t\t\tm_lastImageRenderedSemaphore = VK_NULL_HANDLE;\n\t\t\tm_lastImageAcquiredSemaphore = VK_NULL_HANDLE;\n\n\t\t\tresult = createSwapChain();\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create swap chain error: creating swapchain and image views failed %d: %s", result, getName(result) );\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}\n\n\t\terrorState = ErrorState::SwapChainCreated;\n\n\t\t{\n\t\t\tresult = createAttachments(_commandBuffer);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create swap chain error: creating MSAA/depth attachments failed %d: %s.", result, getName(result) );\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}\n\n\t\terrorState = ErrorState::AttachmentsCreated;\n\n\t\t{\n\t\t\tresult = createFrameBuffer();\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create swap chain error: creating frame buffers failed %d: %s.", result, getName(result) );\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t}\n\n\t\treturn VK_SUCCESS;\n\n\terror:\n\t\tBX_TRACE("errorState %d", errorState);\n\t\tswitch (errorState)\n\t\t{\n\t\tcase ErrorState::AttachmentsCreated:\n\t\t\treleaseAttachments();\n\t\t\tBX_FALLTHROUGH;\n\n\t\tcase ErrorState::SwapChainCreated:\n\t\t\treleaseSwapChain();\n\t\t\tBX_FALLTHROUGH;\n\n\t\tcase ErrorState::SurfaceCreated:\n\t\t\treleaseSurface();\n\t\t\tBX_FALLTHROUGH;\n\n\t\tcase ErrorState::Default:\n\t\t\tbreak;\n\t\t};\n\n\t\treturn VK_SUCCESS != result\n\t\t\t? result\n\t\t\t: VK_ERROR_INITIALIZATION_FAILED\n\t\t\t;\n\t}\n\n\tvoid SwapChainVK::destroy()\n\t{\n\t\tif (VK_NULL_HANDLE != m_swapchain)\n\t\t{\n\t\t\treleaseFrameBuffer();\n\t\t\treleaseAttachments();\n\t\t\treleaseSwapChain();\n\t\t\treleaseSurface();\n\n\t\t\t// can\'t delay-delete the surface, since there can only be one swapchain per surface\n\t\t\t// new framebuffer with the same window would get an error at swapchain creation\n\t\t\ts_renderVK->kick(true);\n\t\t}\n\n\t\tm_nwh = NULL;\n\t}\n\n\tvoid SwapChainVK::update(VkCommandBuffer _commandBuffer, void* _nwh, const Resolution& _resolution)\n\t{\n\t\tconst VkPhysicalDevice physicalDevice = s_renderVK->m_physicalDevice;\n\n\t\tm_lastImageRenderedSemaphore = VK_NULL_HANDLE;\n\t\tm_lastImageAcquiredSemaphore = VK_NULL_HANDLE;\n\n\t\tconst uint64_t recreateSurfaceMask     = BGFX_RESET_HIDPI;\n\t\tconst uint64_t recreateSwapchainMask   = BGFX_RESET_VSYNC | BGFX_RESET_SRGB_BACKBUFFER;\n\t\tconst uint64_t recreateAttachmentsMask = BGFX_RESET_MSAA_MASK;\n\n\t\tconst bool recreateSurface = false\n\t\t\t|| m_needToRecreateSurface\n\t\t\t|| m_nwh != _nwh\n\t\t\t|| (m_resolution.reset & recreateSurfaceMask) != (_resolution.reset & recreateSurfaceMask)\n\t\t\t;\n\n\t\tconst bool recreateSwapchain = false\n\t\t\t|| m_resolution.format != _resolution.format\n\t\t\t|| m_resolution.width  != _resolution.width\n\t\t\t|| m_resolution.height != _resolution.height\n\t\t\t|| (m_resolution.reset & recreateSwapchainMask) != (_resolution.reset & recreateSwapchainMask)\n\t\t\t|| recreateSurface\n\t\t\t;\n\n\t\tconst bool recreateAttachments = false\n\t\t\t|| (m_resolution.reset & recreateAttachmentsMask) != (_resolution.reset & recreateAttachmentsMask)\n\t\t\t|| recreateSwapchain\n\t\t\t;\n\n\t\tm_nwh = _nwh;\n\t\tm_resolution = _resolution;\n\n\t\tif (recreateAttachments)\n\t\t{\n\t\t\treleaseFrameBuffer();\n\t\t\treleaseAttachments();\n\n\t\t\tif (recreateSwapchain)\n\t\t\t{\n\t\t\t\treleaseSwapChain();\n\n\t\t\t\tif (recreateSurface)\n\t\t\t\t{\n\t\t\t\t\tm_sci.oldSwapchain = VK_NULL_HANDLE;\n\t\t\t\t\treleaseSurface();\n\t\t\t\t\ts_renderVK->kick(true);\n\t\t\t\t\t_commandBuffer = s_renderVK->m_commandBuffer;\n\n\t\t\t\t\tVkResult result = createSurface();\n\t\t\t\t\tif (VK_SUCCESS != result)\n\t\t\t\t\t{\n\t\t\t\t\t\tBX_TRACE("Surface lost.");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tVkSurfaceCapabilitiesKHR surfaceCapabilities;\n\t\t\t\tVK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, m_surface, &surfaceCapabilities) );\n\n\t\t\t\tconst uint32_t width = bx::clamp<uint32_t>(\n\t\t\t\t\t  m_resolution.width\n\t\t\t\t\t, surfaceCapabilities.minImageExtent.width\n\t\t\t\t\t, surfaceCapabilities.maxImageExtent.width\n\t\t\t\t\t);\n\t\t\t\tconst uint32_t height = bx::clamp<uint32_t>(\n\t\t\t\t\t  m_resolution.height\n\t\t\t\t\t, surfaceCapabilities.minImageExtent.height\n\t\t\t\t\t, surfaceCapabilities.maxImageExtent.height\n\t\t\t\t\t);\n\n\t\t\t\t// swapchain can\'t have size 0\n\t\t\t\t// on some platforms this happens when minimized\n\t\t\t\tif (width  == 0\n\t\t\t\t||  height == 0)\n\t\t\t\t{\n\t\t\t\t\tm_sci.oldSwapchain = VK_NULL_HANDLE;\n\t\t\t\t\ts_renderVK->kick(true);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tVK_CHECK(createSwapChain() );\n\t\t\t}\n\n\t\t\tVK_CHECK(createAttachments(_commandBuffer) );\n\t\t\tVK_CHECK(createFrameBuffer() );\n\t\t}\n\t}\n\n\tVkResult SwapChainVK::createSurface()\n\t{\n\t\tVkResult result = VK_ERROR_INITIALIZATION_FAILED;\n\n\t\tconst VkInstance instance = s_renderVK->m_instance;\n\t\tconst VkAllocationCallbacks* allocatorCb = s_renderVK->m_allocatorCb;\n\n#if BX_PLATFORM_WINDOWS\n\t\t{\n\t\t\tif (NULL != vkCreateWin32SurfaceKHR)\n\t\t\t{\n\t\t\t\tVkWin32SurfaceCreateInfoKHR sci;\n\t\t\t\tsci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;\n\t\t\t\tsci.pNext = NULL;\n\t\t\t\tsci.flags     = 0;\n\t\t\t\tsci.hinstance = (HINSTANCE)GetModuleHandle(NULL);\n\t\t\t\tsci.hwnd      = (HWND)m_nwh;\n\t\t\t\tresult = vkCreateWin32SurfaceKHR(instance, &sci, allocatorCb, &m_surface);\n\t\t\t}\n\t\t}\n#elif BX_PLATFORM_ANDROID\n\t\t{\n\t\t\tif (NULL != vkCreateAndroidSurfaceKHR)\n\t\t\t{\n\t\t\t\tVkAndroidSurfaceCreateInfoKHR sci;\n\t\t\t\tsci.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;\n\t\t\t\tsci.pNext = NULL;\n\t\t\t\tsci.flags = 0;\n\t\t\t\tsci.window = (ANativeWindow*)m_nwh;\n\t\t\t\tresult = vkCreateAndroidSurfaceKHR(instance, &sci, allocatorCb, &m_surface);\n\t\t\t}\n\t\t}\n#elif BX_PLATFORM_LINUX\n\t\t{\n\t\t\tif (NULL != vkCreateXlibSurfaceKHR)\n\t\t\t{\n\t\t\t\tVkXlibSurfaceCreateInfoKHR sci;\n\t\t\t\tsci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;\n\t\t\t\tsci.pNext = NULL;\n\t\t\t\tsci.flags  = 0;\n\t\t\t\tsci.dpy    = (Display*)g_platformData.ndt;\n\t\t\t\tsci.window = (Window)m_nwh;\n\t\t\t\tresult = vkCreateXlibSurfaceKHR(instance, &sci, allocatorCb, &m_surface);\n\t\t\t}\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tvoid* xcbdll = bx::dlopen("libX11-xcb.so.1");\n\n\t\t\t\tif (NULL != xcbdll\n\t\t\t\t&&  NULL != vkCreateXcbSurfaceKHR)\n\t\t\t\t{\n\t\t\t\t\ttypedef xcb_connection_t* (*PFN_XGETXCBCONNECTION)(Display*);\n\t\t\t\t\tPFN_XGETXCBCONNECTION XGetXCBConnection = (PFN_XGETXCBCONNECTION)bx::dlsym(xcbdll, "XGetXCBConnection");\n\n\t\t\t\t\tunion { void* ptr; xcb_window_t window; } cast = { m_nwh };\n\n\t\t\t\t\tVkXcbSurfaceCreateInfoKHR sci;\n\t\t\t\t\tsci.sType      = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;\n\t\t\t\t\tsci.pNext      = NULL;\n\t\t\t\t\tsci.flags      = 0;\n\t\t\t\t\tsci.connection = XGetXCBConnection( (Display*)g_platformData.ndt);\n\t\t\t\t\tsci.window     = cast.window;\n\t\t\t\t\tresult = vkCreateXcbSurfaceKHR(instance, &sci, allocatorCb, &m_surface);\n\n\t\t\t\t\tbx::dlclose(xcbdll);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#elif BX_PLATFORM_OSX\n\t\t{\n\t\t\tif (NULL != vkCreateMacOSSurfaceMVK)\n\t\t\t{\n\t\t\t\tNSWindow* window    = (NSWindow*)(m_nwh);\n\t\t\t\tNSView* contentView = (NSView*)window.contentView;\n\t\t\t\tCAMetalLayer* layer = [CAMetalLayer layer];\n\n\t\t\t\tif (m_resolution.reset & BGFX_RESET_HIDPI)\n\t\t\t\t{\n\t\t\t\t\tlayer.contentsScale = [window backingScaleFactor];\n\t\t\t\t}\n\n\t\t\t\t[contentView setWantsLayer : YES];\n\t\t\t\t[contentView setLayer : layer];\n\n\t\t\t\tVkMacOSSurfaceCreateInfoMVK sci;\n\t\t\t\tsci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;\n\t\t\t\tsci.pNext = NULL;\n\t\t\t\tsci.flags = 0;\n\t\t\t\tsci.pView = (__bridge void*)layer;\n\t\t\t\tresult = vkCreateMacOSSurfaceMVK(instance, &sci, allocatorCb, &m_surface);\n\t\t\t}\n\t\t}\n#else\n#\terror "Figure out KHR surface..."\n#endif // BX_PLATFORM_\n\n\t\tm_needToRecreateSurface = false;\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create surface error: vkCreate[Platform]SurfaceKHR failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tconst VkPhysicalDevice physicalDevice = s_renderVK->m_physicalDevice;\n\t\tconst uint32_t queueFamily = s_renderVK->m_globalQueueFamily;\n\n\t\tVkBool32 surfaceSupported;\n\t\tresult = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamily, m_surface, &surfaceSupported);\n\n\t\tif (VK_SUCCESS != result\n\t\t||  !surfaceSupported)\n\t\t{\n\t\t\tBX_TRACE("Create surface error: Presentation to the given surface not supported.");\n\t\t\treturn VK_ERROR_INITIALIZATION_FAILED;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tvoid SwapChainVK::releaseSurface()\n\t{\n\t\trelease(m_surface);\n\t}\n\n\tVkResult SwapChainVK::createSwapChain()\n\t{\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tconst VkPhysicalDevice physicalDevice = s_renderVK->m_physicalDevice;\n\t\tconst VkDevice device = s_renderVK->m_device;\n\t\tconst VkAllocationCallbacks* allocatorCb = s_renderVK->m_allocatorCb;\n\n\t\tVkSurfaceCapabilitiesKHR surfaceCapabilities;\n\t\tresult = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, m_surface, &surfaceCapabilities);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create swapchain error: vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tconst uint32_t minSwapBufferCount = bx::max<uint32_t>(surfaceCapabilities.minImageCount, 2);\n\t\tconst uint32_t maxSwapBufferCount = surfaceCapabilities.maxImageCount == 0\n\t\t\t? kMaxBackBuffers\n\t\t\t: bx::min<uint32_t>(surfaceCapabilities.maxImageCount, kMaxBackBuffers)\n\t\t\t;\n\n\t\tif (minSwapBufferCount > maxSwapBufferCount)\n\t\t{\n\t\t\tBX_TRACE("Create swapchain error: Incompatible swapchain image count (min: %d, max: %d, MaxBackBuffers: %d)."\n\t\t\t\t, minSwapBufferCount\n\t\t\t\t, maxSwapBufferCount\n\t\t\t\t, kMaxBackBuffers\n\t\t\t\t);\n\t\t\treturn VK_ERROR_INITIALIZATION_FAILED;\n\t\t}\n\n\t\tconst uint32_t swapBufferCount = bx::clamp<uint32_t>(m_resolution.numBackBuffers, minSwapBufferCount, maxSwapBufferCount);\n\n\t\tconst VkColorSpaceKHR surfaceColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;\n\n\t\tconst bool srgb = !!(m_resolution.reset & BGFX_RESET_SRGB_BACKBUFFER);\n\t\tm_colorFormat = findSurfaceFormat(m_resolution.format, surfaceColorSpace, srgb);\n\n\t\tif (TextureFormat::Count == m_colorFormat)\n\t\t{\n\t\t\tBX_TRACE("Create swapchain error: Unable to find surface format (srgb: %d).", srgb);\n\t\t\treturn VK_ERROR_INITIALIZATION_FAILED;\n\t\t}\n\n\t\tconst VkFormat surfaceFormat = srgb\n\t\t\t? s_textureFormat[m_colorFormat].m_fmtSrgb\n\t\t\t: s_textureFormat[m_colorFormat].m_fmt\n\t\t\t;\n\n\t\tconst uint32_t width = bx::clamp<uint32_t>(\n\t\t\t  m_resolution.width\n\t\t\t, surfaceCapabilities.minImageExtent.width\n\t\t\t, surfaceCapabilities.maxImageExtent.width\n\t\t\t);\n\t\tconst uint32_t height = bx::clamp<uint32_t>(\n\t\t\t  m_resolution.height\n\t\t\t, surfaceCapabilities.minImageExtent.height\n\t\t\t, surfaceCapabilities.maxImageExtent.height\n\t\t\t);\n\n\t\tVkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;\n\n\t\tif (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)\n\t\t{\n\t\t\tcompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;\n\t\t}\n\t\telse if (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)\n\t\t{\n\t\t\tcompositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;\n\t\t}\n\t\telse if (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)\n\t\t{\n\t\t\tcompositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;\n\t\t}\n\n\t\tconst VkImageUsageFlags imageUsageMask = 0\n\t\t\t| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT\n\t\t\t| VK_IMAGE_USAGE_TRANSFER_SRC_BIT\n\t\t\t| VK_IMAGE_USAGE_TRANSFER_DST_BIT\n\t\t\t;\n\t\tconst VkImageUsageFlags imageUsage = surfaceCapabilities.supportedUsageFlags & imageUsageMask;\n\n\t\tm_supportsReadback      = 0 != (imageUsage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT);\n\t\tm_supportsManualResolve = 0 != (imageUsage & VK_IMAGE_USAGE_TRANSFER_DST_BIT);\n\n\t\tconst bool vsync = !!(m_resolution.reset & BGFX_RESET_VSYNC);\n\t\tuint32_t presentModeIdx = findPresentMode(vsync);\n\t\tif (UINT32_MAX == presentModeIdx)\n\t\t{\n\t\t\tBX_TRACE("Create swapchain error: Unable to find present mode (vsync: %d).", vsync);\n\t\t\treturn VK_ERROR_INITIALIZATION_FAILED;\n\t\t}\n\n\t\tm_sci.surface            = m_surface;\n\t\tm_sci.minImageCount      = swapBufferCount;\n\t\tm_sci.imageFormat        = surfaceFormat;\n\t\tm_sci.imageColorSpace    = surfaceColorSpace;\n\t\tm_sci.imageExtent.width  = width;\n\t\tm_sci.imageExtent.height = height;\n\t\tm_sci.imageUsage         = imageUsage;\n\t\tm_sci.compositeAlpha     = compositeAlpha;\n\t\tm_sci.presentMode        = s_presentMode[presentModeIdx].mode;\n\t\tm_sci.clipped            = VK_FALSE;\n\n\t\tresult = vkCreateSwapchainKHR(device, &m_sci, allocatorCb, &m_swapchain);\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create swapchain error: vkCreateSwapchainKHR failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tm_sci.oldSwapchain = m_swapchain;\n\n\t\tresult = vkGetSwapchainImagesKHR(device, m_swapchain, &m_numSwapchainImages, NULL);\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create swapchain error: vkGetSwapchainImagesKHR failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tif (m_numSwapchainImages < m_sci.minImageCount)\n\t\t{\n\t\t\tBX_TRACE("Create swapchain error: vkGetSwapchainImagesKHR: numSwapchainImages %d < minImageCount %d."\n\t\t\t\t, m_numSwapchainImages\n\t\t\t\t, m_sci.minImageCount\n\t\t\t\t);\n\t\t\treturn VK_ERROR_INITIALIZATION_FAILED;\n\t\t}\n\n\t\tif (m_numSwapchainImages > BX_COUNTOF(m_backBufferColorImage) )\n\t\t{\n\t\t\tBX_TRACE("Create swapchain error: vkGetSwapchainImagesKHR: numSwapchainImages %d > countof(m_backBufferColorImage) %d."\n\t\t\t\t, m_numSwapchainImages\n\t\t\t\t, BX_COUNTOF(m_backBufferColorImage)\n\t\t\t\t);\n\t\t\treturn VK_ERROR_INITIALIZATION_FAILED;\n\t\t}\n\n\t\tresult = vkGetSwapchainImagesKHR(device, m_swapchain, &m_numSwapchainImages, &m_backBufferColorImage[0]);\n\t\tif (VK_SUCCESS != result && VK_INCOMPLETE != result)\n\t\t{\n\t\t\tBX_TRACE("Create swapchain error: vkGetSwapchainImagesKHR failed %d: %s."\n\t\t\t\t, result\n\t\t\t\t, getName(result)\n\t\t\t\t);\n\t\t\treturn result;\n\t\t}\n\n\t\tVkImageViewCreateInfo ivci;\n\t\tivci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n\t\tivci.pNext = NULL;\n\t\tivci.flags = 0;\n\t\tivci.viewType = VK_IMAGE_VIEW_TYPE_2D;\n\t\tivci.format   = m_sci.imageFormat;\n\t\tivci.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tivci.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tivci.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tivci.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n\t\tivci.subresourceRange.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;\n\t\tivci.subresourceRange.baseMipLevel   = 0;\n\t\tivci.subresourceRange.levelCount     = 1;\n\t\tivci.subresourceRange.baseArrayLayer = 0;\n\t\tivci.subresourceRange.layerCount     = 1;\n\n\t\tfor (uint32_t ii = 0; ii < m_numSwapchainImages; ++ii)\n\t\t{\n\t\t\tivci.image = m_backBufferColorImage[ii];\n\n\t\t\tresult = vkCreateImageView(device, &ivci, allocatorCb, &m_backBufferColorImageView[ii]);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create swapchain error: vkCreateImageView failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tm_backBufferColorImageLayout[ii] = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\t}\n\n\t\tVkSemaphoreCreateInfo sci;\n\t\tsci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;\n\t\tsci.pNext = NULL;\n\t\tsci.flags = 0;\n\n\t\tfor (uint32_t ii = 0; ii < m_numSwapchainImages; ++ii)\n\t\t{\n\t\t\tif (VK_SUCCESS != vkCreateSemaphore(device, &sci, allocatorCb, &m_presentDoneSemaphore[ii])\n\t\t\t||  VK_SUCCESS != vkCreateSemaphore(device, &sci, allocatorCb, &m_renderDoneSemaphore[ii]) )\n\t\t\t{\n\t\t\t\tBX_TRACE("Create swapchain error: vkCreateSemaphore failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\n\t\tm_backBufferColorIdx = 0;\n\t\tm_currentSemaphore = 0;\n\n\t\tm_needPresent = false;\n\t\tm_needToRefreshSwapchain = false;\n\n\t\treturn result;\n\t}\n\n\tvoid SwapChainVK::releaseSwapChain()\n\t{\n\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_backBufferColorImageView); ++ii)\n\t\t{\n\t\t\trelease(m_backBufferColorImageView[ii]);\n\n\t\t\tm_backBufferFence[ii] = VK_NULL_HANDLE;\n\n\t\t\trelease(m_presentDoneSemaphore[ii]);\n\t\t\trelease(m_renderDoneSemaphore[ii]);\n\t\t}\n\n\t\trelease(m_swapchain);\n\t}\n\n\tVkResult SwapChainVK::createAttachments(VkCommandBuffer _commandBuffer)\n\t{\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tconst uint32_t samplerIndex = (m_resolution.reset & BGFX_RESET_MSAA_MASK) >> BGFX_RESET_MSAA_SHIFT;\n\t\tconst uint64_t textureFlags = (uint64_t(samplerIndex + 1) << BGFX_TEXTURE_RT_MSAA_SHIFT) | BGFX_TEXTURE_RT | BGFX_TEXTURE_RT_WRITE_ONLY;\n\t\tm_sampler = s_msaa[samplerIndex];\n\n\t\tconst uint16_t requiredCaps = m_sampler.Count > 1\n\t\t\t? BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER_MSAA\n\t\t\t: BGFX_CAPS_FORMAT_TEXTURE_FRAMEBUFFER\n\t\t\t;\n\n\t\t// the spec guarantees that at least one of D24S8 and D32FS8 is supported\n\t\tVkFormat depthFormat = VK_FORMAT_D32_SFLOAT_S8_UINT;\n\n\t\tif (g_caps.formats[m_depthFormat] & requiredCaps)\n\t\t{\n\t\t\tdepthFormat = s_textureFormat[m_depthFormat].m_fmtDsv;\n\t\t}\n\t\telse if (g_caps.formats[TextureFormat::D24S8] & requiredCaps)\n\t\t{\n\t\t\tdepthFormat = s_textureFormat[TextureFormat::D24S8].m_fmtDsv;\n\t\t}\n\n\t\tresult = m_backBufferDepthStencil.create(\n\t\t\t  _commandBuffer\n\t\t\t, m_sci.imageExtent.width\n\t\t\t, m_sci.imageExtent.height\n\t\t\t, textureFlags\n\t\t\t, depthFormat\n\t\t\t);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create swapchain error: creating depth stencil image failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tresult = m_backBufferDepthStencil.createView(0, 1, 0, 1, VK_IMAGE_VIEW_TYPE_2D, m_backBufferDepthStencil.m_aspectMask, true, &m_backBufferDepthStencilImageView);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("Create swapchain error: creating depth stencil image view failed %d: %s.", result, getName(result) );\n\t\t\treturn result;\n\t\t}\n\n\t\tif (m_sampler.Count > 1)\n\t\t{\n\t\t\tresult = m_backBufferColorMsaa.create(\n\t\t\t\t  _commandBuffer\n\t\t\t\t, m_sci.imageExtent.width\n\t\t\t\t, m_sci.imageExtent.height\n\t\t\t\t, textureFlags\n\t\t\t\t, m_sci.imageFormat\n\t\t\t\t);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create swapchain error: creating MSAA color image failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tresult = m_backBufferColorMsaa.createView(0, 1, 0, 1, VK_IMAGE_VIEW_TYPE_2D, m_backBufferColorMsaa.m_aspectMask, true, &m_backBufferColorMsaaImageView);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create swapchain error: creating MSAA color image view failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tvoid SwapChainVK::releaseAttachments()\n\t{\n\t\trelease(m_backBufferDepthStencilImageView);\n\t\trelease(m_backBufferColorMsaaImageView);\n\n\t\tm_backBufferDepthStencil.destroy();\n\t\tm_backBufferColorMsaa.destroy();\n\t}\n\n\tVkResult SwapChainVK::createFrameBuffer()\n\t{\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tconst VkDevice device = s_renderVK->m_device;\n\t\tconst VkAllocationCallbacks* allocatorCb = s_renderVK->m_allocatorCb;\n\n\t\tVkRenderPass renderPass;\n\t\tresult = s_renderVK->getRenderPass(*this, &renderPass);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\treturn result;\n\t\t}\n\n\t\tfor (uint32_t ii = 0; ii < m_numSwapchainImages; ++ii)\n\t\t{\n\t\t\tuint32_t numAttachments = 2;\n\t\t\t::VkImageView attachments[3] =\n\t\t\t{\n\t\t\t\tm_sampler.Count > 1\n\t\t\t\t\t? m_backBufferColorMsaaImageView\n\t\t\t\t\t: m_backBufferColorImageView[ii],\n\t\t\t\tm_backBufferDepthStencilImageView,\n\t\t\t};\n\n\t\t\tif (m_sampler.Count > 1 && !m_supportsManualResolve)\n\t\t\t{\n\t\t\t\tattachments[numAttachments++] = m_backBufferColorImageView[ii];\n\t\t\t}\n\n\t\t\tVkFramebufferCreateInfo fci;\n\t\t\tfci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;\n\t\t\tfci.pNext = NULL;\n\t\t\tfci.flags = 0;\n\t\t\tfci.renderPass = renderPass;\n\t\t\tfci.attachmentCount = numAttachments;\n\t\t\tfci.pAttachments = attachments;\n\t\t\tfci.width = m_sci.imageExtent.width;\n\t\t\tfci.height = m_sci.imageExtent.height;\n\t\t\tfci.layers = 1;\n\n\t\t\tresult = vkCreateFramebuffer(device, &fci, allocatorCb, &m_backBufferFrameBuffer[ii]);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tvoid SwapChainVK::releaseFrameBuffer()\n\t{\n\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(m_backBufferColorImageView); ++ii)\n\t\t{\n\t\t\trelease(m_backBufferFrameBuffer[ii]);\n\t\t}\n\t}\n\n\tuint32_t SwapChainVK::findPresentMode(bool _vsync)\n\t{\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tconst VkPhysicalDevice physicalDevice = s_renderVK->m_physicalDevice;\n\n\t\tuint32_t numPresentModes;\n\t\tresult = vkGetPhysicalDeviceSurfacePresentModesKHR(\n\t\t\t  physicalDevice\n\t\t\t, m_surface\n\t\t\t, &numPresentModes\n\t\t\t, NULL\n\t\t\t);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("findPresentMode error: vkGetPhysicalDeviceSurfacePresentModesKHR failed %d: %s.", result, getName(result) );\n\t\t\treturn UINT32_MAX;\n\t\t}\n\n\t\tVkPresentModeKHR presentModes[16];\n\t\tnumPresentModes = bx::min<uint32_t>(numPresentModes, BX_COUNTOF(presentModes) );\n\t\tresult = vkGetPhysicalDeviceSurfacePresentModesKHR(\n\t\t\t  physicalDevice\n\t\t\t, m_surface\n\t\t\t, &numPresentModes\n\t\t\t, presentModes\n\t\t\t);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("findPresentMode error: vkGetPhysicalDeviceSurfacePresentModesKHR failed %d: %s.", result, getName(result) );\n\t\t\treturn UINT32_MAX;\n\t\t}\n\n\t\tuint32_t idx = UINT32_MAX;\n\n\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(s_presentMode) && UINT32_MAX == idx; ++ii)\n\t\t{\n\t\t\tfor (uint32_t jj = 0; jj < numPresentModes; ++jj)\n\t\t\t{\n\t\t\t\tconst PresentMode& pm = s_presentMode[ii];\n\n\t\t\t\tif (pm.mode  == presentModes[jj]\n\t\t\t\t&&  pm.vsync == _vsync)\n\t\t\t\t{\n\t\t\t\t\tidx = ii;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (UINT32_MAX == idx)\n\t\t{\n\t\t\tidx = 0;\n\t\t\tBX_TRACE("Present mode not found! Defaulting to %s.", s_presentMode[idx].name);\n\t\t}\n\n\t\treturn idx;\n\t}\n\n\tTextureFormat::Enum SwapChainVK::findSurfaceFormat(TextureFormat::Enum _format, VkColorSpaceKHR _colorSpace, bool _srgb)\n\t{\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tTextureFormat::Enum selectedFormat = TextureFormat::Count;\n\n\t\tconst VkPhysicalDevice physicalDevice = s_renderVK->m_physicalDevice;\n\n\t\tuint32_t numSurfaceFormats;\n\t\tresult = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, m_surface, &numSurfaceFormats, NULL);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("findSurfaceFormat error: vkGetPhysicalDeviceSurfaceFormatsKHR failed %d: %s.", result, getName(result) );\n\t\t\treturn selectedFormat;\n\t\t}\n\n\t\tVkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)BX_ALLOC(g_allocator, numSurfaceFormats * sizeof(VkSurfaceFormatKHR) );\n\t\tresult = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, m_surface, &numSurfaceFormats, surfaceFormats);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\tBX_TRACE("findSurfaceFormat error: vkGetPhysicalDeviceSurfaceFormatsKHR failed %d: %s.", result, getName(result) );\n\t\t\tBX_FREE(g_allocator, surfaceFormats);\n\t\t\treturn selectedFormat;\n\t\t}\n\n\t\tconst TextureFormat::Enum requestedFormats[] =\n\t\t{\n\t\t\t_format,\n\t\t\tTextureFormat::BGRA8,\n\t\t\tTextureFormat::RGBA8,\n\t\t};\n\n\t\tfor (uint32_t ii = 0; ii < BX_COUNTOF(requestedFormats) && TextureFormat::Count == selectedFormat; ii++)\n\t\t{\n\t\t\tconst TextureFormat::Enum requested = requestedFormats[ii];\n\t\t\tconst VkFormat requestedVkFormat = _srgb\n\t\t\t\t? s_textureFormat[requested].m_fmtSrgb\n\t\t\t\t: s_textureFormat[requested].m_fmt\n\t\t\t\t;\n\n\t\t\tfor (uint32_t jj = 0; jj < numSurfaceFormats; jj++)\n\t\t\t{\n\t\t\t\tif (_colorSpace == surfaceFormats[jj].colorSpace\n\t\t\t\t&&  requestedVkFormat == surfaceFormats[jj].format)\n\t\t\t\t{\n\t\t\t\t\tselectedFormat = requested;\n\t\t\t\t\tif (0 != ii\n\t\t\t\t\t&&  s_renderVK->m_swapchainFormats[_format] != selectedFormat)\n\t\t\t\t\t{\n\t\t\t\t\t\ts_renderVK->m_swapchainFormats[_format] = selectedFormat;\n\t\t\t\t\t\tBX_TRACE(\n\t\t\t\t\t\t\t"findSurfaceFormat: Surface format %s not found! Defaulting to %s."\n\t\t\t\t\t\t\t, bimg::getName(bimg::TextureFormat::Enum(_format) )\n\t\t\t\t\t\t\t, bimg::getName(bimg::TextureFormat::Enum(selectedFormat) )\n\t\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tBX_FREE(g_allocator, surfaceFormats);\n\n\t\tif (TextureFormat::Count == selectedFormat)\n\t\t{\n\t\t\tBX_TRACE("findSurfaceFormat error: No supported surface format found.");\n\t\t}\n\n\t\treturn selectedFormat;\n\t}\n\n\tbool SwapChainVK::acquire(VkCommandBuffer _commandBuffer)\n\t{\n\t\tif (VK_NULL_HANDLE == m_swapchain\n\t\t||  m_needToRefreshSwapchain)\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!m_needPresent)\n\t\t{\n\t\t\tconst VkDevice device = s_renderVK->m_device;\n\n\t\t\tm_lastImageAcquiredSemaphore = m_presentDoneSemaphore[m_currentSemaphore];\n\t\t\tm_lastImageRenderedSemaphore = m_renderDoneSemaphore[m_currentSemaphore];\n\t\t\tm_currentSemaphore = (m_currentSemaphore + 1) % m_numSwapchainImages;\n\n\t\t\tVkResult result = vkAcquireNextImageKHR(\n\t\t\t\t  device\n\t\t\t\t, m_swapchain\n\t\t\t\t, UINT64_MAX\n\t\t\t\t, m_lastImageAcquiredSemaphore\n\t\t\t\t, VK_NULL_HANDLE\n\t\t\t\t, &m_backBufferColorIdx\n\t\t\t\t);\n\n\t\t\tswitch (result)\n\t\t\t{\n\t\t\tcase VK_SUCCESS:\n\t\t\t\tbreak;\n\n\t\t\tcase VK_ERROR_SURFACE_LOST_KHR:\n\t\t\t\tm_needToRecreateSurface = true;\n\t\t\t\tBX_FALLTHROUGH;\n\n\t\t\tcase VK_ERROR_OUT_OF_DATE_KHR:\n\t\t\tcase VK_SUBOPTIMAL_KHR:\n\t\t\t\tm_needToRefreshSwapchain = true;\n\t\t\t\treturn false;\n\n\t\t\tdefault:\n\t\t\t\tBX_ASSERT(VK_SUCCESS == result, "vkAcquireNextImageKHR(...); VK error 0x%x: %s", result, getName(result) );\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (VK_NULL_HANDLE != m_backBufferFence[m_backBufferColorIdx])\n\t\t\t{\n\t\t\t\tVK_CHECK(vkWaitForFences(\n\t\t\t\t\t  device\n\t\t\t\t\t, 1\n\t\t\t\t\t, &m_backBufferFence[m_backBufferColorIdx]\n\t\t\t\t\t, VK_TRUE\n\t\t\t\t\t, UINT64_MAX\n\t\t\t\t\t) );\n\t\t\t}\n\n\t\t\ttransitionImage(_commandBuffer);\n\n\t\t\tm_needPresent = true;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tvoid SwapChainVK::present()\n\t{\n\t\tif (VK_NULL_HANDLE != m_swapchain\n\t\t&&  m_needPresent)\n\t\t{\n\t\t\tVkPresentInfoKHR pi;\n\t\t\tpi.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;\n\t\t\tpi.pNext = NULL;\n\t\t\tpi.waitSemaphoreCount = 1;\n\t\t\tpi.pWaitSemaphores    = &m_lastImageRenderedSemaphore;\n\t\t\tpi.swapchainCount     = 1;\n\t\t\tpi.pSwapchains        = &m_swapchain;\n\t\t\tpi.pImageIndices      = &m_backBufferColorIdx;\n\t\t\tpi.pResults           = NULL;\n\t\t\tVkResult result = vkQueuePresentKHR(m_queue, &pi);\n\n\t\t\tswitch (result)\n\t\t\t{\n\t\t\tcase VK_ERROR_SURFACE_LOST_KHR:\n\t\t\t\tm_needToRecreateSurface = true;\n\t\t\t\tBX_FALLTHROUGH;\n\n\t\t\tcase VK_ERROR_OUT_OF_DATE_KHR:\n\t\t\tcase VK_SUBOPTIMAL_KHR:\n\t\t\t\tm_needToRefreshSwapchain = true;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tBX_ASSERT(VK_SUCCESS == result, "vkQueuePresentKHR(...); VK error 0x%x: %s", result, getName(result) );\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tm_needPresent = false;\n\t\t\tm_lastImageRenderedSemaphore = VK_NULL_HANDLE;\n\t\t}\n\t}\n\n\tvoid SwapChainVK::transitionImage(VkCommandBuffer _commandBuffer)\n\t{\n\t\tVkImageLayout& layout = m_backBufferColorImageLayout[m_backBufferColorIdx];\n\n\t\tconst bool toPresent = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout;\n\n\t\tconst VkImageLayout newLayout = toPresent\n\t\t\t? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR\n\t\t\t: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL\n\t\t\t;\n\n\t\tlayout = toPresent ? layout : VK_IMAGE_LAYOUT_UNDEFINED;\n\n\t\tsetImageMemoryBarrier(\n\t\t\t  _commandBuffer\n\t\t\t, m_backBufferColorImage[m_backBufferColorIdx]\n\t\t\t, VK_IMAGE_ASPECT_COLOR_BIT\n\t\t\t, layout\n\t\t\t, newLayout\n\t\t);\n\n\t\tlayout = newLayout;\n\t}\n\n\tvoid FrameBufferVK::create(uint8_t _num, const Attachment* _attachment)\n\t{\n\t\tm_numTh = _num;\n\t\tbx::memCopy(m_attachment, _attachment, sizeof(Attachment) * _num);\n\n\t\tpostReset();\n\t}\n\n\tVkResult FrameBufferVK::create(uint16_t _denseIdx, void* _nwh, uint32_t _width, uint32_t _height, TextureFormat::Enum _format, TextureFormat::Enum _depthFormat)\n\t{\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tResolution resolution = s_renderVK->m_resolution;\n\t\tresolution.format = TextureFormat::Count == _format ? resolution.format : _format;\n\t\tresolution.width  = _width;\n\t\tresolution.height = _height;\n\t\tif (_denseIdx != UINT16_MAX)\n\t\t{\n\t\t\tresolution.reset &= ~BGFX_RESET_MSAA_MASK;\n\t\t}\n\n\t\tresult = m_swapChain.create(s_renderVK->m_commandBuffer, _nwh, resolution, _depthFormat);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\treturn result;\n\t\t}\n\n\t\tresult = s_renderVK->getRenderPass(m_swapChain, &m_renderPass);\n\n\t\tif (VK_SUCCESS != result)\n\t\t{\n\t\t\treturn result;\n\t\t}\n\n\t\tm_denseIdx = _denseIdx;\n\t\tm_nwh = _nwh;\n\t\tm_width = _width;\n\t\tm_height = _height;\n\t\tm_sampler = m_swapChain.m_sampler;\n\n\t\treturn result;\n\t}\n\n\tvoid FrameBufferVK::preReset()\n\t{\n\t\tif (VK_NULL_HANDLE != m_framebuffer)\n\t\t{\n\t\t\ts_renderVK->release(m_framebuffer);\n\n\t\t\tfor (uint8_t ii = 0; ii < m_numTh; ++ii)\n\t\t\t{\n\t\t\t\ts_renderVK->release(m_textureImageViews[ii]);\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid FrameBufferVK::postReset()\n\t{\n\t\tif (m_numTh > 0)\n\t\t{\n\t\t\tconst VkDevice device = s_renderVK->m_device;\n\t\t\tconst VkAllocationCallbacks* allocatorCb = s_renderVK->m_allocatorCb;\n\n\t\t\tVK_CHECK(s_renderVK->getRenderPass(m_numTh, m_attachment, &m_renderPass) );\n\n\t\t\tm_depth = BGFX_INVALID_HANDLE;\n\t\t\tm_num = 0;\n\n\t\t\tfor (uint8_t ii = 0; ii < m_numTh; ++ii)\n\t\t\t{\n\t\t\t\tconst Attachment& at = m_attachment[ii];\n\t\t\t\tconst TextureVK& texture = s_renderVK->m_textures[at.handle.idx];\n\t\t\t\tVK_CHECK(texture.createView(\n\t\t\t\t\t  at.layer\n\t\t\t\t\t, at.numLayers\n\t\t\t\t\t, at.mip\n\t\t\t\t\t, 1\n\t\t\t\t\t, at.numLayers > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D\n\t\t\t\t\t, texture.m_aspectMask\n\t\t\t\t\t, true\n\t\t\t\t\t, &m_textureImageViews[ii]\n\t\t\t\t\t) );\n\n\t\t\t\tif (texture.m_aspectMask & VK_IMAGE_ASPECT_COLOR_BIT)\n\t\t\t\t{\n\t\t\t\t\tm_texture[m_num] = at.handle;\n\t\t\t\t\tm_num++;\n\t\t\t\t}\n\t\t\t\telse if (texture.m_aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) )\n\t\t\t\t{\n\t\t\t\t\tm_depth = at.handle;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst TextureVK& firstTexture = s_renderVK->m_textures[m_attachment[0].handle.idx];\n\t\t\tm_width  = bx::uint32_max(firstTexture.m_width  >> m_attachment[0].mip, 1);\n\t\t\tm_height = bx::uint32_max(firstTexture.m_height >> m_attachment[0].mip, 1);\n\t\t\tm_sampler = firstTexture.m_sampler;\n\n\t\t\tVkFramebufferCreateInfo fci;\n\t\t\tfci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;\n\t\t\tfci.pNext = NULL;\n\t\t\tfci.flags = 0;\n\t\t\tfci.renderPass      = m_renderPass;\n\t\t\tfci.attachmentCount = m_numTh;\n\t\t\tfci.pAttachments    = &m_textureImageViews[0];\n\t\t\tfci.width  = m_width;\n\t\t\tfci.height = m_height;\n\t\t\tfci.layers = m_attachment[0].numLayers;\n\n\t\t\tVK_CHECK(vkCreateFramebuffer(device, &fci, allocatorCb, &m_framebuffer) );\n\n\t\t\tm_currentFramebuffer = m_framebuffer;\n\t\t}\n\t}\n\n\tvoid FrameBufferVK::update(VkCommandBuffer _commandBuffer, const Resolution& _resolution)\n\t{\n\t\tm_swapChain.update(_commandBuffer, m_nwh, _resolution);\n\t\tVK_CHECK(s_renderVK->getRenderPass(m_swapChain, &m_renderPass) );\n\t\tm_width   = _resolution.width;\n\t\tm_height  = _resolution.height;\n\t\tm_sampler = m_swapChain.m_sampler;\n\t}\n\n\tvoid FrameBufferVK::resolve()\n\t{\n\t\tif (!m_needResolve)\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tif (NULL == m_nwh)\n\t\t{\n\t\t\tfor (uint32_t ii = 0; ii < m_numTh; ++ii)\n\t\t\t{\n\t\t\t\tconst Attachment& at = m_attachment[ii];\n\n\t\t\t\tif (isValid(at.handle) )\n\t\t\t\t{\n\t\t\t\t\tTextureVK& texture = s_renderVK->m_textures[at.handle.idx];\n\t\t\t\t\ttexture.resolve(s_renderVK->m_commandBuffer, at.resolve, at.layer, at.numLayers, at.mip);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (isRenderable()\n\t\t&&       m_sampler.Count > 1\n\t\t&&       m_swapChain.m_supportsManualResolve)\n\t\t{\n\t\t\tm_swapChain.m_backBufferColorMsaa.m_singleMsaaImage = m_swapChain.m_backBufferColorImage[m_swapChain.m_backBufferColorIdx];\n\t\t\tm_swapChain.m_backBufferColorMsaa.m_currentSingleMsaaImageLayout = m_swapChain.m_backBufferColorImageLayout[m_swapChain.m_backBufferColorIdx];\n\n\t\t\tm_swapChain.m_backBufferColorMsaa.resolve(s_renderVK->m_commandBuffer, 0, 0, 1, 0);\n\n\t\t\tm_swapChain.m_backBufferColorMsaa.m_singleMsaaImage = VK_NULL_HANDLE;\n\t\t\tm_swapChain.m_backBufferColorMsaa.m_currentSingleMsaaImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n\t\t}\n\n\t\tm_needResolve = false;\n\t}\n\n\tuint16_t FrameBufferVK::destroy()\n\t{\n\t\tpreReset();\n\n\t\tif (NULL != m_nwh)\n\t\t{\n\t\t\tm_swapChain.destroy();\n\t\t\tm_nwh = NULL;\n\t\t\tm_needPresent = false;\n\t\t}\n\n\t\tm_numTh = 0;\n\t\tm_num = 0;\n\t\tm_depth = BGFX_INVALID_HANDLE;\n\n\t\tm_needResolve = false;\n\n\t\tuint16_t denseIdx = m_denseIdx;\n\t\tm_denseIdx = UINT16_MAX;\n\t\treturn denseIdx;\n\t}\n\n\tbool FrameBufferVK::acquire(VkCommandBuffer _commandBuffer)\n\t{\n\t\tbool acquired = true;\n\n\t\tif (NULL != m_nwh)\n\t\t{\n\t\t\tacquired = m_swapChain.acquire(_commandBuffer);\n\t\t\tm_needPresent = m_swapChain.m_needPresent;\n\t\t\tm_currentFramebuffer = m_swapChain.m_backBufferFrameBuffer[m_swapChain.m_backBufferColorIdx];\n\t\t}\n\n\t\tm_needResolve = true;\n\n\t\treturn acquired;\n\t}\n\n\tvoid FrameBufferVK::present()\n\t{\n\t\tm_swapChain.present();\n\t\tm_needPresent = false;\n\t}\n\n\tbool FrameBufferVK::isRenderable() const\n\t{\n\t\treturn false\n\t\t\t|| (NULL == m_nwh)\n\t\t\t|| m_swapChain.m_needPresent\n\t\t\t;\n\t}\n\n\tVkResult CommandQueueVK::init(uint32_t _queueFamily, VkQueue _queue, uint32_t _numFramesInFlight)\n\t{\n\t\tm_queueFamily = _queueFamily;\n\t\tm_queue = _queue;\n\t\tm_numFramesInFlight = bx::clamp<uint32_t>(_numFramesInFlight, 1, BGFX_CONFIG_MAX_FRAME_LATENCY);\n\t\tm_activeCommandBuffer = VK_NULL_HANDLE;\n\n\t\treturn reset();\n\t}\n\n\tVkResult CommandQueueVK::reset()\n\t{\n\t\tshutdown();\n\n\t\tm_currentFrameInFlight = 0;\n\t\tm_consumeIndex         = 0;\n\n\t\tm_numSignalSemaphores = 0;\n\t\tm_numWaitSemaphores   = 0;\n\n\t\tm_activeCommandBuffer = VK_NULL_HANDLE;\n\t\tm_currentFence        = VK_NULL_HANDLE;\n\t\tm_completedFence      = VK_NULL_HANDLE;\n\n\t\tm_submitted = 0;\n\n\t\tVkCommandPoolCreateInfo cpci;\n\t\tcpci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;\n\t\tcpci.pNext = NULL;\n\t\tcpci.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;\n\t\tcpci.queueFamilyIndex = m_queueFamily;\n\n\t\tVkCommandBufferAllocateInfo cbai;\n\t\tcbai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;\n\t\tcbai.pNext = NULL;\n\t\tcbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n\t\tcbai.commandBufferCount = 1;\n\n\t\tVkFenceCreateInfo fci;\n\t\tfci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;\n\t\tfci.pNext = NULL;\n\t\tfci.flags = VK_FENCE_CREATE_SIGNALED_BIT;\n\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tfor (uint32_t ii = 0; ii < m_numFramesInFlight; ++ii)\n\t\t{\n\t\t\tresult = vkCreateCommandPool(\n\t\t\t\t  s_renderVK->m_device\n\t\t\t\t, &cpci\n\t\t\t\t, s_renderVK->m_allocatorCb\n\t\t\t\t, &m_commandList[ii].m_commandPool\n\t\t\t\t);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create command queue error: vkCreateCommandPool failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tcbai.commandPool = m_commandList[ii].m_commandPool;\n\n\t\t\tresult = vkAllocateCommandBuffers(\n\t\t\t\t  s_renderVK->m_device\n\t\t\t\t, &cbai\n\t\t\t\t, &m_commandList[ii].m_commandBuffer\n\t\t\t\t);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create command queue error: vkAllocateCommandBuffers failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tresult = vkCreateFence(\n\t\t\t\t  s_renderVK->m_device\n\t\t\t\t, &fci\n\t\t\t\t, s_renderVK->m_allocatorCb\n\t\t\t\t, &m_commandList[ii].m_fence\n\t\t\t\t);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Create command queue error: vkCreateFence failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tvoid CommandQueueVK::shutdown()\n\t{\n\t\tkick(true);\n\t\tfinish(true);\n\n\t\tfor (uint32_t ii = 0; ii < m_numFramesInFlight; ++ii)\n\t\t{\n\t\t\tvkDestroy(m_commandList[ii].m_fence);\n\t\t\tm_commandList[ii].m_commandBuffer = VK_NULL_HANDLE;\n\t\t\tvkDestroy(m_commandList[ii].m_commandPool);\n\t\t}\n\t}\n\n\tVkResult CommandQueueVK::alloc(VkCommandBuffer* _commandBuffer)\n\t{\n\t\tVkResult result = VK_SUCCESS;\n\n\t\tif (m_activeCommandBuffer == VK_NULL_HANDLE)\n\t\t{\n\t\t\tconst VkDevice device = s_renderVK->m_device;\n\t\t\tCommandList& commandList = m_commandList[m_currentFrameInFlight];\n\n\t\t\tresult = vkWaitForFences(device, 1, &commandList.m_fence, VK_TRUE, UINT64_MAX);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Allocate command buffer error: vkWaitForFences failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tresult = vkResetCommandPool(device, commandList.m_commandPool, 0);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Allocate command buffer error: vkResetCommandPool failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tVkCommandBufferBeginInfo cbi;\n\t\t\tcbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n\t\t\tcbi.pNext = NULL;\n\t\t\tcbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;\n\t\t\tcbi.pInheritanceInfo = NULL;\n\n\t\t\tresult = vkBeginCommandBuffer(commandList.m_commandBuffer, &cbi);\n\n\t\t\tif (VK_SUCCESS != result)\n\t\t\t{\n\t\t\t\tBX_TRACE("Allocate command buffer error: vkBeginCommandBuffer failed %d: %s.", result, getName(result) );\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tm_activeCommandBuffer = commandList.m_commandBuffer;\n\t\t\tm_currentFence = commandList.m_fence;\n\t\t}\n\n\t\tif (NULL != _commandBuffer)\n\t\t{\n\t\t\t*_commandBuffer = m_activeCommandBuffer;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tvoid CommandQueueVK::addWaitSemaphore(VkSemaphore _semaphore, VkPipelineStageFlags _waitFlags)\n\t{\n\t\tBX_ASSERT(m_numWaitSemaphores < BX_COUNTOF(m_waitSemaphores), "Too many wait semaphores.");\n\n\t\tm_waitSemaphores[m_numWaitSemaphores]      = _semaphore;\n\t\tm_waitSemaphoreStages[m_numWaitSemaphores] = _waitFlags;\n\t\tm_numWaitSemaphores++;\n\t}\n\n\tvoid CommandQueueVK::addSignalSemaphore(VkSemaphore _semaphore)\n\t{\n\t\tBX_ASSERT(m_numSignalSemaphores < BX_COUNTOF(m_signalSemaphores), "Too many signal semaphores.");\n\n\t\tm_signalSemaphores[m_numSignalSemaphores] = _semaphore;\n\t\tm_numSignalSemaphores++;\n\t}\n\n\tvoid CommandQueueVK::kick(bool _wait)\n\t{\n\t\tif (VK_NULL_HANDLE != m_activeCommandBuffer)\n\t\t{\n\t\t\tconst VkDevice device = s_renderVK->m_device;\n\n\t\t\tsetMemoryBarrier(\n\t\t\t\t  m_activeCommandBuffer\n\t\t\t\t, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT\n\t\t\t\t, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT\n\t\t\t\t);\n\n\t\t\tVK_CHECK(vkEndCommandBuffer(m_activeCommandBuffer) );\n\n\t\t\tm_completedFence = m_currentFence;\n\t\t\tm_currentFence = VK_NULL_HANDLE;\n\n\t\t\tVK_CHECK(vkResetFences(device, 1, &m_completedFence) );\n\n\t\t\tVkSubmitInfo si;\n\t\t\tsi.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n\t\t\tsi.pNext = NULL;\n\t\t\tsi.waitSemaphoreCount    = m_numWaitSemaphores;\n\t\t\tsi.pWaitSemaphores       = &m_waitSemaphores[0];\n\t\t\tsi.pWaitDstStageMask     = m_waitSemaphoreStages;\n\t\t\tsi.commandBufferCount    = 1;\n\t\t\tsi.pCommandBuffers       = &m_activeCommandBuffer;\n\t\t\tsi.signalSemaphoreCount  = m_numSignalSemaphores;\n\t\t\tsi.pSignalSemaphores     = &m_signalSemaphores[0];\n\n\t\t\tm_numWaitSemaphores   = 0;\n\t\t\tm_numSignalSemaphores = 0;\n\n\t\t\tVK_CHECK(vkQueueSubmit(m_queue, 1, &si, m_completedFence) );\n\n\t\t\tif (_wait)\n\t\t\t{\n\t\t\t\tVK_CHECK(vkWaitForFences(device, 1, &m_completedFence, VK_TRUE, UINT64_MAX) );\n\t\t\t}\n\n\t\t\tm_activeCommandBuffer = VK_NULL_HANDLE;\n\n\t\t\tm_currentFrameInFlight = (m_currentFrameInFlight + 1) % m_numFramesInFlight;\n\t\t\tm_submitted++;\n\t\t}\n\t}\n\n\tvoid CommandQueueVK::finish(bool _finishAll)\n\t{\n\t\tif (_finishAll)\n\t\t{\n\t\t\tfor (uint32_t ii = 0; ii < m_numFramesInFlight; ++ii)\n\t\t\t{\n\t\t\t\tconsume();\n\t\t\t}\n\n\t\t\tm_consumeIndex = m_currentFrameInFlight;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconsume();\n\t\t}\n\t}\n\n\tvoid CommandQueueVK::release(uint64_t _handle, VkObjectType _type)\n\t{\n\t\tResource resource;\n\t\tresource.m_type = _type;\n\t\tresource.m_handle = _handle;\n\t\tm_release[m_currentFrameInFlight].push_back(resource);\n\t}\n\n\tvoid CommandQueueVK::consume()\n\t{\n\t\tm_consumeIndex = (m_consumeIndex + 1) % m_numFramesInFlight;\n\n\t\tfor (const Resource& resource : m_release[m_consumeIndex])\n\t\t{\n\t\t\tswitch (resource.m_type)\n\t\t\t{\n\t\t\tcase VK_OBJECT_TYPE_BUFFER:                destroy<VkBuffer             >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_IMAGE_VIEW:            destroy<VkImageView          >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_IMAGE:                 destroy<VkImage              >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_FRAMEBUFFER:           destroy<VkFramebuffer        >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_PIPELINE_LAYOUT:       destroy<VkPipelineLayout     >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_PIPELINE:              destroy<VkPipeline           >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_DESCRIPTOR_SET:        destroy<VkDescriptorSet      >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT: destroy<VkDescriptorSetLayout>(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_RENDER_PASS:           destroy<VkRenderPass         >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_SAMPLER:               destroy<VkSampler            >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_SEMAPHORE:             destroy<VkSemaphore          >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_SURFACE_KHR:           destroy<VkSurfaceKHR         >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_SWAPCHAIN_KHR:         destroy<VkSwapchainKHR       >(resource.m_handle); break;\n\t\t\tcase VK_OBJECT_TYPE_DEVICE_MEMORY:         destroy<VkDeviceMemory       >(resource.m_handle); break;\n\t\t\tdefault:\n\t\t\t\tBX_ASSERT(false, "Invalid resource type: %d", resource.m_type);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tm_release[m_consumeIndex].clear();\n\t}\n\n\tvoid RendererContextVK::submitBlit(BlitState& _bs, uint16_t _view)\n\t{\n\t\tVkImageLayout srcLayouts[BGFX_CONFIG_MAX_BLIT_ITEMS];\n\t\tVkImageLayout dstLayouts[BGFX_CONFIG_MAX_BLIT_ITEMS];\n\n\t\tBlitState bs0 = _bs;\n\n\t\twhile (bs0.hasItem(_view) )\n\t\t{\n\t\t\tuint16_t item = bs0.m_item;\n\n\t\t\tconst BlitItem& blit = bs0.advance();\n\n\t\t\tTextureVK& src = m_textures[blit.m_src.idx];\n\t\t\tTextureVK& dst = m_textures[blit.m_dst.idx];\n\n\t\t\tsrcLayouts[item] = VK_NULL_HANDLE != src.m_singleMsaaImage ? src.m_currentSingleMsaaImageLayout : src.m_currentImageLayout;\n\t\t\tdstLayouts[item] = dst.m_currentImageLayout;\n\t\t}\n\n\t\tbs0 = _bs;\n\n\t\twhile (bs0.hasItem(_view) )\n\t\t{\n\t\t\tconst BlitItem& blit = bs0.advance();\n\n\t\t\tTextureVK& src = m_textures[blit.m_src.idx];\n\t\t\tTextureVK& dst = m_textures[blit.m_dst.idx];\n\n\t\t\tsrc.setImageMemoryBarrier(\n\t\t\t\t  m_commandBuffer\n\t\t\t\t, blit.m_src.idx == blit.m_dst.idx ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL\n\t\t\t\t, VK_NULL_HANDLE != src.m_singleMsaaImage\n\t\t\t\t);\n\n\t\t\tif (blit.m_src.idx != blit.m_dst.idx)\n\t\t\t{\n\t\t\t\tdst.setImageMemoryBarrier(m_commandBuffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);\n\t\t\t}\n\n\t\t\tconst uint16_t srcSamples = VK_NULL_HANDLE != src.m_singleMsaaImage ? 1 : src.m_sampler.Count;\n\t\t\tconst uint16_t dstSamples = dst.m_sampler.Count;\n\t\t\tBX_UNUSED(srcSamples, dstSamples);\n\n\t\t\tBX_ASSERT(\n\t\t\t\t  srcSamples == dstSamples\n\t\t\t\t, "Mismatching texture sample count (%d != %d)."\n\t\t\t\t, srcSamples\n\t\t\t\t, dstSamples\n\t\t\t\t);\n\n\t\t\tVkImageCopy copyInfo;\n\t\t\tcopyInfo.srcSubresource.aspectMask     = src.m_aspectMask;\n\t\t\tcopyInfo.srcSubresource.mipLevel       = blit.m_srcMip;\n\t\t\tcopyInfo.srcSubresource.baseArrayLayer = 0;\n\t\t\tcopyInfo.srcSubresource.layerCount     = 1;\n\t\t\tcopyInfo.srcOffset.x = blit.m_srcX;\n\t\t\tcopyInfo.srcOffset.y = blit.m_srcY;\n\t\t\tcopyInfo.srcOffset.z = 0;\n\t\t\tcopyInfo.dstSubresource.aspectMask     = dst.m_aspectMask;\n\t\t\tcopyInfo.dstSubresource.mipLevel       = blit.m_dstMip;\n\t\t\tcopyInfo.dstSubresource.baseArrayLayer = 0;\n\t\t\tcopyInfo.dstSubresource.layerCount     = 1;\n\t\t\tcopyInfo.dstOffset.x = blit.m_dstX;\n\t\t\tcopyInfo.dstOffset.y = blit.m_dstY;\n\t\t\tcopyInfo.dstOffset.z = 0;\n\t\t\tcopyInfo.extent.width  = blit.m_width;\n\t\t\tcopyInfo.extent.height = blit.m_height;\n\t\t\tcopyInfo.extent.depth  = 1;\n\n\t\t\tconst uint32_t depth = bx::max<uint32_t>(1, blit.m_depth);\n\n\t\t\tif (VK_IMAGE_VIEW_TYPE_3D == src.m_type)\n\t\t\t{\n\t\t\t\tBX_ASSERT(VK_IMAGE_VIEW_TYPE_3D == dst.m_type, "Can\'t blit between 2D and 3D image.");\n\n\t\t\t\tcopyInfo.srcOffset.z  = blit.m_srcZ;\n\t\t\t\tcopyInfo.dstOffset.z  = blit.m_dstZ;\n\t\t\t\tcopyInfo.extent.depth = depth;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tcopyInfo.srcSubresource.baseArrayLayer = blit.m_srcZ;\n\t\t\t\tcopyInfo.dstSubresource.baseArrayLayer = blit.m_dstZ;\n\t\t\t\tcopyInfo.srcSubresource.layerCount = depth;\n\t\t\t\tcopyInfo.dstSubresource.layerCount = depth;\n\t\t\t}\n\n\t\t\tvkCmdCopyImage(\n\t\t\t\t  m_commandBuffer\n\t\t\t\t, VK_NULL_HANDLE != src.m_singleMsaaImage ? src.m_singleMsaaImage : src.m_textureImage\n\t\t\t\t, VK_NULL_HANDLE != src.m_singleMsaaImage ? src.m_currentSingleMsaaImageLayout : src.m_currentImageLayout\n\t\t\t\t, dst.m_textureImage\n\t\t\t\t, dst.m_currentImageLayout\n\t\t\t\t, 1\n\t\t\t\t, &copyInfo\n\t\t\t\t);\n\n\t\t\tsetMemoryBarrier(\n\t\t\t\t  m_commandBuffer\n\t\t\t\t, VK_PIPELINE_STAGE_TRANSFER_BIT\n\t\t\t\t, VK_PIPELINE_STAGE_TRANSFER_BIT\n\t\t\t\t);\n\t\t}\n\n\t\twhile (_bs.hasItem(_view) )\n\t\t{\n\t\t\tuint16_t item = _bs.m_item;\n\n\t\t\tconst BlitItem& blit = _bs.advance();\n\n\t\t\tTextureVK& src = m_textures[blit.m_src.idx];\n\t\t\tTextureVK& dst = m_textures[blit.m_dst.idx];\n\n\t\t\tsrc.setImageMemoryBarrier(m_commandBuffer, srcLayouts[item], VK_NULL_HANDLE != src.m_singleMsaaImage);\n\t\t\tdst.setImageMemoryBarrier(m_commandBuffer, dstLayouts[item]);\n\t\t}\n\t}\n\n\tvoid RendererContextVK::submit(Frame* _render, ClearQuad& _clearQuad, TextVideoMemBlitter& _textVideoMemBlitter)\n\t{\n\t\tBX_UNUSED(_clearQuad);\n\n\t\tif (updateResolution(_render->m_resolution) )\n\t\t{\n\t\t\treturn;\n\t\t}\n\n\t\tif (_render->m_capture)\n\t\t{\n\t\t\trenderDocTriggerCapture();\n\t\t}\n\n\t\tBGFX_VK_PROFILER_BEGIN_LITERAL("rendererSubmit", kColorView);\n\n\t\tint64_t timeBegin = bx::getHPCounter();\n\t\tint64_t captureElapsed = 0;\n\n\t\tuint32_t frameQueryIdx = UINT32_MAX;\n\n\t\tif (m_timerQuerySupport)\n\t\t{\n\t\t\tframeQueryIdx = m_gpuTimer.begin(BGFX_CONFIG_MAX_VIEWS, _render->m_frameNum);\n\t\t}\n\n\t\tif (0 < _render->m_iboffset)\n\t\t{\n\t\t\tBGFX_PROFILER_SCOPE("bgfx/Update transient index buffer", kColorResource);\n\t\t\tTransientIndexBuffer* ib = _render->m_transientIb;\n\t\t\tm_indexBuffers[ib->handle.idx].update(m_commandBuffer, 0, _render->m_iboffset, ib->data);\n\t\t}\n\n\t\tif (0 < _render->m_vboffset)\n\t\t{\n\t\t\tBGFX_PROFILER_SCOPE("bgfx/Update transient vertex buffer", kColorResource);\n\t\t\tTransientVertexBuffer* vb = _render->m_transientVb;\n\t\t\tm_vertexBuffers[vb->handle.idx].update(m_commandBuffer, 0, _render->m_vboffset, vb->data);\n\t\t}\n\n\t\t_render->sort();\n\n\t\tRenderDraw currentState;\n\t\tcurrentState.clear();\n\t\tcurrentState.m_stateFlags = BGFX_STATE_NONE;\n\t\tcurrentState.m_stencil    = packStencil(BGFX_STENCIL_NONE, BGFX_STENCIL_NONE);\n\n\t\tstatic ViewState viewState;\n\t\tviewState.reset(_render);\n\n\t\tbool wireframe = !!(_render->m_debug&BGFX_DEBUG_WIREFRAME);\n\t\tsetDebugWireframe(wireframe);\n\n\t\tProgramHandle currentProgram = BGFX_INVALID_HANDLE;\n\t\tbool hasPredefined = false;\n\t\tVkPipeline currentPipeline = VK_NULL_HANDLE;\n\t\tVkDescriptorSet currentDescriptorSet = VK_NULL_HANDLE;\n\t\tuint32_t currentBindHash = 0;\n\t\tuint32_t descriptorSetCount = 0;\n\t\tVkIndexType currentIndexFormat = VK_INDEX_TYPE_MAX_ENUM;\n\t\tSortKey key;\n\t\tuint16_t view = UINT16_MAX;\n\t\tFrameBufferHandle fbh = { BGFX_CONFIG_MAX_FRAME_BUFFERS };\n\n\t\tBlitState bs(_render);\n\n\t\tuint64_t blendFactor = UINT64_MAX;\n\n\t\tbool wasCompute     = false;\n\t\tbool viewHasScissor = false;\n\t\tbool restoreScissor = false;\n\t\tRect viewScissorRect;\n\t\tviewScissorRect.clear();\n\n\t\tbool isFrameBufferValid = false;\n\n\t\tuint32_t statsNumPrimsSubmitted[BX_COUNTOF(s_primInfo)] = {};\n\t\tuint32_t statsNumPrimsRendered[BX_COUNTOF(s_primInfo)] = {};\n\t\tuint32_t statsNumInstances[BX_COUNTOF(s_primInfo)] = {};\n\t\tuint32_t statsNumIndices = 0;\n\t\tuint32_t statsKeyType[2] = {};\n\n\t\tconst uint64_t f0 = BGFX_STATE_BLEND_FACTOR;\n\t\tconst uint64_t f1 = BGFX_STATE_BLEND_INV_FACTOR;\n\t\tconst uint64_t f2 = BGFX_STATE_BLEND_FACTOR<<4;\n\t\tconst uint64_t f3 = BGFX_STATE_BLEND_INV_FACTOR<<4;\n\n\t\tScratchBufferVK& scratchBuffer = m_scratchBuffer[m_cmd.m_currentFrameInFlight];\n\t\tscratchBuffer.reset();\n\n\t\tsetMemoryBarrier(\n\t\t\t  m_commandBuffer\n\t\t\t, VK_PIPELINE_STAGE_TRANSFER_BIT\n\t\t\t, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT\n\t\t\t);\n\n\t\tVkRenderPassBeginInfo rpbi;\n\t\trpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;\n\t\trpbi.pNext = NULL;\n\t\trpbi.clearValueCount = 0;\n\t\trpbi.pClearValues    = NULL;\n\n\t\tbool beginRenderPass = false;\n\n\t\tProfiler<TimerQueryVK> profiler(\n\t\t\t  _render\n\t\t\t, m_gpuTimer\n\t\t\t, s_viewName\n\t\t\t, m_timerQuerySupport\n\t\t\t);\n\n\t\tm_occlusionQuery.flush(_render);\n\n\t\tif (0 == (_render->m_debug&BGFX_DEBUG_IFH) )\n\t\t{\n\t\t\tviewState.m_rect = _render->m_view[0].m_rect;\n\n\t\t\tint32_t numItems = _render->m_numRenderItems;\n\t\t\tfor (int32_t item = 0; item < numItems;)\n\t\t\t{\n\t\t\t\tconst uint64_t encodedKey = _render->m_sortKeys[item];\n\t\t\t\tconst bool isCompute = key.decode(encodedKey, _render->m_viewRemap);\n\t\t\t\tstatsKeyType[isCompute]++;\n\n\t\t\t\tconst bool viewChanged = 0\n\t\t\t\t\t|| key.m_view != view\n\t\t\t\t\t|| item == numItems\n\t\t\t\t\t;\n\n\t\t\t\tconst uint32_t itemIdx       = _render->m_sortValues[item];\n\t\t\t\tconst RenderItem& renderItem = _render->m_renderItem[itemIdx];\n\t\t\t\tconst RenderBind& renderBind = _render->m_renderItemBind[itemIdx];\n\t\t\t\t++item;\n\n\t\t\t\tif (viewChanged)\n\t\t\t\t{\n\t\t\t\t\tif (beginRenderPass)\n\t\t\t\t\t{\n\t\t\t\t\t\tvkCmdEndRenderPass(m_commandBuffer);\n\t\t\t\t\t\tbeginRenderPass = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tview = key.m_view;\n\t\t\t\t\tcurrentProgram = BGFX_INVALID_HANDLE;\n\t\t\t\t\thasPredefined  = false;\n\n\t\t\t\t\tif (item > 1)\n\t\t\t\t\t{\n\t\t\t\t\t\tprofiler.end();\n\t\t\t\t\t}\n\n\t\t\t\t\tBGFX_VK_PROFILER_END();\n\t\t\t\t\tsetViewType(view, " ");\n\t\t\t\t\tBGFX_VK_PROFILER_BEGIN(view, kColorView);\n\n\t\t\t\t\tprofiler.begin(view);\n\n\t\t\t\t\tif (_render->m_view[view].m_fbh.idx != fbh.idx)\n\t\t\t\t\t{\n\t\t\t\t\t\tfbh = _render->m_view[view].m_fbh;\n\t\t\t\t\t\tsetFrameBuffer(fbh);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst FrameBufferVK& fb = isValid(m_fbh)\n\t\t\t\t\t\t? m_frameBuffers[m_fbh.idx]\n\t\t\t\t\t\t: m_backBuffer\n\t\t\t\t\t\t;\n\n\t\t\t\t\tisFrameBufferValid = fb.isRenderable();\n\n\t\t\t\t\tif (isFrameBufferValid)\n\t\t\t\t\t{\n\t\t\t\t\t\tviewState.m_rect = _render->m_view[view].m_rect;\n\t\t\t\t\t\tconst Rect& rect        = _render->m_view[view].m_rect;\n\t\t\t\t\t\tconst Rect& scissorRect = _render->m_view[view].m_scissor;\n\t\t\t\t\t\tviewHasScissor  = !scissorRect.isZero();\n\t\t\t\t\t\tviewScissorRect = viewHasScissor ? scissorRect : rect;\n\t\t\t\t\t\trestoreScissor = false;\n\n\t\t\t\t\t\trpbi.framebuffer = fb.m_currentFramebuffer;\n\t\t\t\t\t\trpbi.renderPass  = fb.m_renderPass;\n\t\t\t\t\t\trpbi.renderArea.offset.x = rect.m_x;\n\t\t\t\t\t\trpbi.renderArea.offset.y = rect.m_y;\n\t\t\t\t\t\trpbi.renderArea.extent.width  = rect.m_width;\n\t\t\t\t\t\trpbi.renderArea.extent.height = rect.m_height;\n\n\t\t\t\t\t\tVkViewport vp;\n\t\t\t\t\t\tvp.x        =  float(rect.m_x);\n\t\t\t\t\t\tvp.y        =  float(rect.m_y + rect.m_height);\n\t\t\t\t\t\tvp.width    =  float(rect.m_width);\n\t\t\t\t\t\tvp.height   = -float(rect.m_height);\n\t\t\t\t\t\tvp.minDepth = 0.0f;\n\t\t\t\t\t\tvp.maxDepth = 1.0f;\n\t\t\t\t\t\tvkCmdSetViewport(m_commandBuffer, 0, 1, &vp);\n\n\t\t\t\t\t\tVkRect2D rc;\n\t\t\t\t\t\trc.offset.x      = viewScissorRect.m_x;\n\t\t\t\t\t\trc.offset.y      = viewScissorRect.m_y;\n\t\t\t\t\t\trc.extent.width  = viewScissorRect.m_width;\n\t\t\t\t\t\trc.extent.height = viewScissorRect.m_height;\n\t\t\t\t\t\tvkCmdSetScissor(m_commandBuffer, 0, 1, &rc);\n\n\t\t\t\t\t\tconst Clear& clr = _render->m_view[view].m_clear;\n\t\t\t\t\t\tif (BGFX_CLEAR_NONE != clr.m_flags)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvkCmdBeginRenderPass(m_commandBuffer, &rpbi, VK_SUBPASS_CONTENTS_INLINE);\n\n\t\t\t\t\t\t\tRect clearRect = rect;\n\t\t\t\t\t\t\tclearRect.setIntersect(rect, viewScissorRect);\n\t\t\t\t\t\t\tclearQuad(clearRect, clr, _render->m_colorPalette);\n\n\t\t\t\t\t\t\tvkCmdEndRenderPass(m_commandBuffer);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tsubmitBlit(bs, view);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (isCompute)\n\t\t\t\t{\n\t\t\t\t\tif (!wasCompute)\n\t\t\t\t\t{\n\t\t\t\t\t\twasCompute = true;\n\t\t\t\t\t\tcurrentBindHash = 0;\n\n\t\t\t\t\t\tBGFX_VK_PROFILER_END();\n\t\t\t\t\t\tsetViewType(view, "C");\n\t\t\t\t\t\tBGFX_VK_PROFILER_BEGIN(view, kColorCompute);\n\t\t\t\t\t}\n\n\t\t\t\t\t// renderpass external subpass dependencies handle graphics -> compute and compute -> graphics\n\t\t\t\t\t// but not compute -> compute (possibly also across views if they contain no draw calls)\n\t\t\t\t\tsetMemoryBarrier(\n\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT\n\t\t\t\t\t\t, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT\n\t\t\t\t\t);\n\n\t\t\t\t\tconst RenderCompute& compute = renderItem.compute;\n\n\t\t\t\t\tconst VkPipeline pipeline = getPipeline(key.m_program);\n\n\t\t\t\t\tif (currentPipeline != pipeline)\n\t\t\t\t\t{\n\t\t\t\t\t\tcurrentPipeline = pipeline;\n\t\t\t\t\t\tvkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);\n\t\t\t\t\t}\n\n\t\t\t\t\tbool constantsChanged = false;\n\n\t\t\t\t\tif (compute.m_uniformBegin < compute.m_uniformEnd\n\t\t\t\t\t||  currentProgram.idx != key.m_program.idx)\n\t\t\t\t\t{\n\t\t\t\t\t\trendererUpdateUniforms(this, _render->m_uniformBuffer[compute.m_uniformIdx], compute.m_uniformBegin, compute.m_uniformEnd);\n\n\t\t\t\t\t\tcurrentProgram = key.m_program;\n\t\t\t\t\t\tProgramVK& program = m_program[currentProgram.idx];\n\n\t\t\t\t\t\tUniformBuffer* vcb = program.m_vsh->m_constantBuffer;\n\n\t\t\t\t\t\tif (NULL != vcb)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommit(*vcb);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\thasPredefined = 0 < program.m_numPredefined;\n\t\t\t\t\t\tconstantsChanged = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst ProgramVK& program = m_program[currentProgram.idx];\n\n\t\t\t\t\tif (constantsChanged\n\t\t\t\t\t||  hasPredefined)\n\t\t\t\t\t{\n\t\t\t\t\t\tviewState.setPredefined<4>(this, view, program, _render, compute);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (VK_NULL_HANDLE != program.m_descriptorSetLayout)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst uint32_t vsize = program.m_vsh->m_size;\n\t\t\t\t\t\tuint32_t numOffset = 0;\n\t\t\t\t\t\tuint32_t offset = 0;\n\n\t\t\t\t\t\tif (constantsChanged\n\t\t\t\t\t\t||  hasPredefined)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (vsize > 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\toffset = scratchBuffer.write(m_vsScratch, vsize);\n\t\t\t\t\t\t\t\t++numOffset;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbx::HashMurmur2A hash;\n\t\t\t\t\t\thash.begin();\n\t\t\t\t\t\thash.add(program.m_descriptorSetLayout);\n\t\t\t\t\t\thash.add(renderBind.m_bind, sizeof(renderBind.m_bind) );\n\t\t\t\t\t\thash.add(vsize);\n\t\t\t\t\t\thash.add(0);\n\t\t\t\t\t\tconst uint32_t bindHash = hash.end();\n\n\t\t\t\t\t\tif (currentBindHash != bindHash)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcurrentBindHash = bindHash;\n\n\t\t\t\t\t\t\tcurrentDescriptorSet = getDescriptorSet(\n\t\t\t\t\t\t\t\t  program\n\t\t\t\t\t\t\t\t, renderBind\n\t\t\t\t\t\t\t\t, scratchBuffer\n\t\t\t\t\t\t\t\t, _render->m_colorPalette\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tdescriptorSetCount++;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvkCmdBindDescriptorSets(\n\t\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t\t, VK_PIPELINE_BIND_POINT_COMPUTE\n\t\t\t\t\t\t\t, program.m_pipelineLayout\n\t\t\t\t\t\t\t, 0\n\t\t\t\t\t\t\t, 1\n\t\t\t\t\t\t\t, &currentDescriptorSet\n\t\t\t\t\t\t\t, numOffset\n\t\t\t\t\t\t\t, &offset\n\t\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (isValid(compute.m_indirectBuffer) )\n\t\t\t\t\t{\n\t\t\t\t\t\tconst VertexBufferVK& vb = m_vertexBuffers[compute.m_indirectBuffer.idx];\n\n\t\t\t\t\t\tuint32_t numDrawIndirect = UINT16_MAX == compute.m_numIndirect\n\t\t\t\t\t\t\t? vb.m_size/BGFX_CONFIG_DRAW_INDIRECT_STRIDE\n\t\t\t\t\t\t\t: compute.m_numIndirect\n\t\t\t\t\t\t\t;\n\n\t\t\t\t\t\tuint32_t args = compute.m_startIndirect * BGFX_CONFIG_DRAW_INDIRECT_STRIDE;\n\t\t\t\t\t\tfor (uint32_t ii = 0; ii < numDrawIndirect; ++ii)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvkCmdDispatchIndirect(m_commandBuffer, vb.m_buffer, args);\n\t\t\t\t\t\t\targs += BGFX_CONFIG_DRAW_INDIRECT_STRIDE;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tvkCmdDispatch(m_commandBuffer, compute.m_numX, compute.m_numY, compute.m_numZ);\n\t\t\t\t\t}\n\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst RenderDraw& draw = renderItem.draw;\n\n\t\t\t\trendererUpdateUniforms(this, _render->m_uniformBuffer[draw.m_uniformIdx], draw.m_uniformBegin, draw.m_uniformEnd);\n\n\t\t\t\tconst bool hasOcclusionQuery = 0 != (draw.m_stateFlags & BGFX_STATE_INTERNAL_OCCLUSION_QUERY);\n\t\t\t\t{\n\t\t\t\t\tconst bool occluded = true\n\t\t\t\t\t\t&& isValid(draw.m_occlusionQuery)\n\t\t\t\t\t\t&& !hasOcclusionQuery\n\t\t\t\t\t\t&& !isVisible(_render, draw.m_occlusionQuery, 0 != (draw.m_submitFlags & BGFX_SUBMIT_INTERNAL_OCCLUSION_VISIBLE) )\n\t\t\t\t\t\t;\n\n\t\t\t\t\tif (occluded\n\t\t\t\t\t||  !isFrameBufferValid\n\t\t\t\t\t||  0 == draw.m_streamMask\n\t\t\t\t\t||  _render->m_frameCache.isZeroArea(viewScissorRect, draw.m_scissor) )\n\t\t\t\t\t{\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst uint64_t changedFlags = currentState.m_stateFlags ^ draw.m_stateFlags;\n\t\t\t\tcurrentState.m_stateFlags = draw.m_stateFlags;\n\n\t\t\t\tif (!beginRenderPass)\n\t\t\t\t{\n\t\t\t\t\tif (wasCompute)\n\t\t\t\t\t{\n\t\t\t\t\t\twasCompute = false;\n\t\t\t\t\t\tcurrentBindHash = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tBGFX_VK_PROFILER_END();\n\t\t\t\t\tsetViewType(view, " ");\n\t\t\t\t\tBGFX_VK_PROFILER_BEGIN(view, kColorDraw);\n\n\t\t\t\t\tvkCmdBeginRenderPass(m_commandBuffer, &rpbi, VK_SUBPASS_CONTENTS_INLINE);\n\t\t\t\t\tbeginRenderPass = true;\n\n\t\t\t\t\tcurrentProgram = BGFX_INVALID_HANDLE;\n\t\t\t\t\tcurrentState.m_scissor = !draw.m_scissor;\n\t\t\t\t}\n\n\t\t\t\tif (0 != draw.m_streamMask)\n\t\t\t\t{\n\t\t\t\t\tconst bool bindAttribs = hasVertexStreamChanged(currentState, draw);\n\n\t\t\t\t\tcurrentState.m_streamMask         = draw.m_streamMask;\n\t\t\t\t\tcurrentState.m_instanceDataBuffer = draw.m_instanceDataBuffer;\n\t\t\t\t\tcurrentState.m_instanceDataOffset = draw.m_instanceDataOffset;\n\t\t\t\t\tcurrentState.m_instanceDataStride = draw.m_instanceDataStride;\n\n\t\t\t\t\tconst VertexLayout* layouts[BGFX_CONFIG_MAX_VERTEX_STREAMS];\n\t\t\t\t\tVkBuffer streamBuffers[BGFX_CONFIG_MAX_VERTEX_STREAMS + 1];\n\t\t\t\t\tVkDeviceSize streamOffsets[BGFX_CONFIG_MAX_VERTEX_STREAMS + 1];\n\t\t\t\t\tuint8_t numStreams = 0;\n\t\t\t\t\tuint32_t numVertices = draw.m_numVertices;\n\t\t\t\t\tif (UINT8_MAX != draw.m_streamMask)\n\t\t\t\t\t{\n\t\t\t\t\t\tfor (uint32_t idx = 0, streamMask = draw.m_streamMask\n\t\t\t\t\t\t\t; 0 != streamMask\n\t\t\t\t\t\t\t; streamMask >>= 1, idx += 1, ++numStreams\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst uint32_t ntz = bx::uint32_cnttz(streamMask);\n\t\t\t\t\t\t\tstreamMask >>= ntz;\n\t\t\t\t\t\t\tidx         += ntz;\n\n\t\t\t\t\t\t\tcurrentState.m_stream[idx] = draw.m_stream[idx];\n\n\t\t\t\t\t\t\tconst VertexBufferHandle handle = draw.m_stream[idx].m_handle;\n\t\t\t\t\t\t\tconst VertexBufferVK& vb = m_vertexBuffers[handle.idx];\n\t\t\t\t\t\t\tconst uint16_t decl = isValid(draw.m_stream[idx].m_layoutHandle)\n\t\t\t\t\t\t\t\t? draw.m_stream[idx].m_layoutHandle.idx\n\t\t\t\t\t\t\t\t: vb.m_layoutHandle.idx\n\t\t\t\t\t\t\t\t;\n\t\t\t\t\t\t\tconst VertexLayout& layout = m_vertexLayouts[decl];\n\t\t\t\t\t\t\tconst uint32_t stride = layout.m_stride;\n\n\t\t\t\t\t\t\tstreamBuffers[numStreams] = m_vertexBuffers[handle.idx].m_buffer;\n\t\t\t\t\t\t\tstreamOffsets[numStreams] = draw.m_stream[idx].m_startVertex * stride;\n\t\t\t\t\t\t\tlayouts[numStreams]       = &layout;\n\n\t\t\t\t\t\t\tnumVertices = bx::uint32_min(UINT32_MAX == draw.m_numVertices\n\t\t\t\t\t\t\t\t? vb.m_size/stride\n\t\t\t\t\t\t\t\t: draw.m_numVertices\n\t\t\t\t\t\t\t\t, numVertices\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (bindAttribs)\n\t\t\t\t\t{\n\t\t\t\t\t\tuint32_t numVertexBuffers = numStreams;\n\n\t\t\t\t\t\tif (isValid(draw.m_instanceDataBuffer) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstreamOffsets[numVertexBuffers] = draw.m_instanceDataOffset;\n\t\t\t\t\t\t\tstreamBuffers[numVertexBuffers] = m_vertexBuffers[draw.m_instanceDataBuffer.idx].m_buffer;\n\t\t\t\t\t\t\tnumVertexBuffers++;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (0 < numVertexBuffers)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvkCmdBindVertexBuffers(\n\t\t\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t\t\t, 0\n\t\t\t\t\t\t\t\t, numVertexBuffers\n\t\t\t\t\t\t\t\t, &streamBuffers[0]\n\t\t\t\t\t\t\t\t, streamOffsets\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst VkPipeline pipeline =\n\t\t\t\t\t\tgetPipeline(draw.m_stateFlags\n\t\t\t\t\t\t\t, draw.m_stencil\n\t\t\t\t\t\t\t, numStreams\n\t\t\t\t\t\t\t, layouts\n\t\t\t\t\t\t\t, key.m_program\n\t\t\t\t\t\t\t, uint8_t(draw.m_instanceDataStride/16)\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\tif (currentPipeline != pipeline)\n\t\t\t\t\t{\n\t\t\t\t\t\tcurrentPipeline = pipeline;\n\t\t\t\t\t\tvkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst bool hasStencil = 0 != draw.m_stencil;\n\n\t\t\t\t\tif (hasStencil\n\t\t\t\t\t&&  currentState.m_stencil != draw.m_stencil)\n\t\t\t\t\t{\n\t\t\t\t\t\tcurrentState.m_stencil = draw.m_stencil;\n\n\t\t\t\t\t\tconst uint32_t fstencil = unpackStencil(0, draw.m_stencil);\n\t\t\t\t\t\tconst uint32_t ref = (fstencil&BGFX_STENCIL_FUNC_REF_MASK)>>BGFX_STENCIL_FUNC_REF_SHIFT;\n\t\t\t\t\t\tvkCmdSetStencilReference(m_commandBuffer, VK_STENCIL_FRONT_AND_BACK, ref);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst bool hasFactor = 0\n\t\t\t\t\t\t|| f0 == (draw.m_stateFlags & f0)\n\t\t\t\t\t\t|| f1 == (draw.m_stateFlags & f1)\n\t\t\t\t\t\t|| f2 == (draw.m_stateFlags & f2)\n\t\t\t\t\t\t|| f3 == (draw.m_stateFlags & f3)\n\t\t\t\t\t\t;\n\n\t\t\t\t\tif (hasFactor\n\t\t\t\t\t&&  blendFactor != draw.m_rgba)\n\t\t\t\t\t{\n\t\t\t\t\t\tblendFactor = draw.m_rgba;\n\n\t\t\t\t\t\tfloat bf[4];\n\t\t\t\t\t\tbf[0] = ( (draw.m_rgba>>24)     )/255.0f;\n\t\t\t\t\t\tbf[1] = ( (draw.m_rgba>>16)&0xff)/255.0f;\n\t\t\t\t\t\tbf[2] = ( (draw.m_rgba>> 8)&0xff)/255.0f;\n\t\t\t\t\t\tbf[3] = ( (draw.m_rgba    )&0xff)/255.0f;\n\t\t\t\t\t\tvkCmdSetBlendConstants(m_commandBuffer, bf);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst uint16_t scissor = draw.m_scissor;\n\n\t\t\t\t\tif (currentState.m_scissor != scissor)\n\t\t\t\t\t{\n\t\t\t\t\t\tcurrentState.m_scissor = scissor;\n\n\t\t\t\t\t\tif (UINT16_MAX == scissor)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (restoreScissor\n\t\t\t\t\t\t\t||  viewHasScissor)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\trestoreScissor = false;\n\t\t\t\t\t\t\t\tVkRect2D rc;\n\t\t\t\t\t\t\t\trc.offset.x      = viewScissorRect.m_x;\n\t\t\t\t\t\t\t\trc.offset.y      = viewScissorRect.m_y;\n\t\t\t\t\t\t\t\trc.extent.width  = viewScissorRect.m_width;\n\t\t\t\t\t\t\t\trc.extent.height = viewScissorRect.m_height;\n\t\t\t\t\t\t\t\tvkCmdSetScissor(m_commandBuffer, 0, 1, &rc);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\trestoreScissor = true;\n\t\t\t\t\t\t\tRect scissorRect;\n\t\t\t\t\t\t\tscissorRect.setIntersect(viewScissorRect, _render->m_frameCache.m_rectCache.m_cache[scissor]);\n\n\t\t\t\t\t\t\tVkRect2D rc;\n\t\t\t\t\t\t\trc.offset.x      = scissorRect.m_x;\n\t\t\t\t\t\t\trc.offset.y      = scissorRect.m_y;\n\t\t\t\t\t\t\trc.extent.width  = scissorRect.m_width;\n\t\t\t\t\t\t\trc.extent.height = scissorRect.m_height;\n\t\t\t\t\t\t\tvkCmdSetScissor(m_commandBuffer, 0, 1, &rc);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tbool constantsChanged = false;\n\t\t\t\t\tif (draw.m_uniformBegin < draw.m_uniformEnd\n\t\t\t\t\t||  currentProgram.idx != key.m_program.idx\n\t\t\t\t\t||  BGFX_STATE_ALPHA_REF_MASK & changedFlags)\n\t\t\t\t\t{\n\t\t\t\t\t\tcurrentProgram = key.m_program;\n\t\t\t\t\t\tProgramVK& program = m_program[currentProgram.idx];\n\n\t\t\t\t\t\tUniformBuffer* vcb = program.m_vsh->m_constantBuffer;\n\t\t\t\t\t\tif (NULL != vcb)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcommit(*vcb);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (NULL != program.m_fsh)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tUniformBuffer* fcb = program.m_fsh->m_constantBuffer;\n\t\t\t\t\t\t\tif (NULL != fcb)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcommit(*fcb);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\thasPredefined = 0 < program.m_numPredefined;\n\t\t\t\t\t\tconstantsChanged = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst ProgramVK& program = m_program[currentProgram.idx];\n\n\t\t\t\t\tif (hasPredefined)\n\t\t\t\t\t{\n\t\t\t\t\t\tuint32_t ref = (draw.m_stateFlags & BGFX_STATE_ALPHA_REF_MASK) >> BGFX_STATE_ALPHA_REF_SHIFT;\n\t\t\t\t\t\tviewState.m_alphaRef = ref / 255.0f;\n\t\t\t\t\t\tviewState.setPredefined<4>(this, view, program, _render, draw);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (VK_NULL_HANDLE != program.m_descriptorSetLayout)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst uint32_t vsize = program.m_vsh->m_size;\n\t\t\t\t\t\tconst uint32_t fsize = NULL != program.m_fsh ? program.m_fsh->m_size : 0;\n\t\t\t\t\t\tuint32_t numOffset = 0;\n\t\t\t\t\t\tuint32_t offsets[2] = { 0, 0 };\n\n\t\t\t\t\t\tif (constantsChanged\n\t\t\t\t\t\t||  hasPredefined)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (vsize > 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\toffsets[numOffset++] = scratchBuffer.write(m_vsScratch, vsize);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (fsize > 0)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\toffsets[numOffset++] = scratchBuffer.write(m_fsScratch, fsize);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbx::HashMurmur2A hash;\n\t\t\t\t\t\thash.begin();\n\t\t\t\t\t\thash.add(program.m_descriptorSetLayout);\n\t\t\t\t\t\thash.add(renderBind.m_bind, sizeof(renderBind.m_bind) );\n\t\t\t\t\t\thash.add(vsize);\n\t\t\t\t\t\thash.add(fsize);\n\t\t\t\t\t\tconst uint32_t bindHash = hash.end();\n\n\t\t\t\t\t\tif (currentBindHash != bindHash)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcurrentBindHash = bindHash;\n\n\t\t\t\t\t\t\tcurrentDescriptorSet = getDescriptorSet(\n\t\t\t\t\t\t\t\t  program\n\t\t\t\t\t\t\t\t, renderBind\n\t\t\t\t\t\t\t\t, scratchBuffer\n\t\t\t\t\t\t\t\t, _render->m_colorPalette\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tdescriptorSetCount++;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvkCmdBindDescriptorSets(\n\t\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t\t, VK_PIPELINE_BIND_POINT_GRAPHICS\n\t\t\t\t\t\t\t, program.m_pipelineLayout\n\t\t\t\t\t\t\t, 0\n\t\t\t\t\t\t\t, 1\n\t\t\t\t\t\t\t, &currentDescriptorSet\n\t\t\t\t\t\t\t, numOffset\n\t\t\t\t\t\t\t, offsets\n\t\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tVkBuffer bufferIndirect = VK_NULL_HANDLE;\n\t\t\t\t\tVkBuffer bufferNumIndirect = VK_NULL_HANDLE;\n\t\t\t\t\tuint32_t numDrawIndirect = 0;\n\t\t\t\t\tuint32_t bufferOffsetIndirect = 0;\n\t\t\t\t\tuint32_t bufferNumOffsetIndirect = 0;\n\t\t\t\t\tif (isValid(draw.m_indirectBuffer) )\n\t\t\t\t\t{\n\t\t\t\t\t\tconst VertexBufferVK& vb = m_vertexBuffers[draw.m_indirectBuffer.idx];\n\t\t\t\t\t\tbufferIndirect = vb.m_buffer;\n\t\t\t\t\t\tnumDrawIndirect = UINT16_MAX == draw.m_numIndirect\n\t\t\t\t\t\t\t? vb.m_size / BGFX_CONFIG_DRAW_INDIRECT_STRIDE\n\t\t\t\t\t\t\t: draw.m_numIndirect\n\t\t\t\t\t\t\t;\n\t\t\t\t\t\tbufferOffsetIndirect = draw.m_startIndirect * BGFX_CONFIG_DRAW_INDIRECT_STRIDE;\n\n\t\t\t\t\t\tif (isValid(draw.m_numIndirectBuffer) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tbufferNumIndirect = m_indexBuffers[draw.m_numIndirectBuffer.idx].m_buffer;\n\t\t\t\t\t\t\tbufferNumOffsetIndirect = draw.m_numIndirectIndex * sizeof(uint32_t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (hasOcclusionQuery)\n\t\t\t\t\t{\n\t\t\t\t\t\tm_occlusionQuery.begin(draw.m_occlusionQuery);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst uint8_t primIndex = uint8_t((draw.m_stateFlags & BGFX_STATE_PT_MASK) >> BGFX_STATE_PT_SHIFT);\n\t\t\t\t\tconst PrimInfo& prim = s_primInfo[primIndex];\n\n\t\t\t\t\tuint32_t numPrimsSubmitted = 0;\n\t\t\t\t\tuint32_t numIndices = 0;\n\n\t\t\t\t\tif (!isValid(draw.m_indexBuffer) )\n\t\t\t\t\t{\n\t\t\t\t\t\tnumPrimsSubmitted = numVertices / prim.m_div - prim.m_sub;\n\n\t\t\t\t\t\tif (isValid(draw.m_indirectBuffer) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (isValid(draw.m_numIndirectBuffer) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tvkCmdDrawIndirectCountKHR(\n\t\t\t\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t\t\t\t, bufferIndirect\n\t\t\t\t\t\t\t\t\t, bufferOffsetIndirect\n\t\t\t\t\t\t\t\t\t, bufferNumIndirect\n\t\t\t\t\t\t\t\t\t, bufferNumOffsetIndirect\n\t\t\t\t\t\t\t\t\t, numDrawIndirect\n\t\t\t\t\t\t\t\t\t, BGFX_CONFIG_DRAW_INDIRECT_STRIDE\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tvkCmdDrawIndirect(\n\t\t\t\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t\t\t\t, bufferIndirect\n\t\t\t\t\t\t\t\t\t, bufferOffsetIndirect\n\t\t\t\t\t\t\t\t\t, numDrawIndirect\n\t\t\t\t\t\t\t\t\t, BGFX_CONFIG_DRAW_INDIRECT_STRIDE\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvkCmdDraw(\n\t\t\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t\t\t, numVertices\n\t\t\t\t\t\t\t\t, draw.m_numInstances\n\t\t\t\t\t\t\t\t, 0\n\t\t\t\t\t\t\t\t, 0\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tconst bool isIndex16          = draw.isIndex16();\n\t\t\t\t\t\tconst uint32_t indexSize      = isIndex16 ? 2 : 4;\n\t\t\t\t\t\tconst VkIndexType indexFormat = isIndex16 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32;\n\t\t\t\t\t\tconst BufferVK& ib            = m_indexBuffers[draw.m_indexBuffer.idx];\n\n\t\t\t\t\t\tnumIndices = UINT32_MAX == draw.m_numIndices\n\t\t\t\t\t\t\t? ib.m_size / indexSize\n\t\t\t\t\t\t\t: draw.m_numIndices\n\t\t\t\t\t\t\t;\n\n\t\t\t\t\t\tnumPrimsSubmitted = numIndices / prim.m_div - prim.m_sub;\n\n\t\t\t\t\t\tif (currentState.m_indexBuffer.idx != draw.m_indexBuffer.idx\n\t\t\t\t\t\t||  currentIndexFormat != indexFormat)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tcurrentState.m_indexBuffer = draw.m_indexBuffer;\n\t\t\t\t\t\t\tcurrentIndexFormat = indexFormat;\n\n\t\t\t\t\t\t\tvkCmdBindIndexBuffer(\n\t\t\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t\t\t, m_indexBuffers[draw.m_indexBuffer.idx].m_buffer\n\t\t\t\t\t\t\t\t, 0\n\t\t\t\t\t\t\t\t, indexFormat\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (isValid(draw.m_indirectBuffer) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (isValid(draw.m_numIndirectBuffer) )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tvkCmdDrawIndexedIndirectCountKHR(\n\t\t\t\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t\t\t\t, bufferIndirect\n\t\t\t\t\t\t\t\t\t, bufferOffsetIndirect\n\t\t\t\t\t\t\t\t\t, bufferNumIndirect\n\t\t\t\t\t\t\t\t\t, bufferNumOffsetIndirect\n\t\t\t\t\t\t\t\t\t, numDrawIndirect\n\t\t\t\t\t\t\t\t\t, BGFX_CONFIG_DRAW_INDIRECT_STRIDE\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tvkCmdDrawIndexedIndirect(\n\t\t\t\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t\t\t\t, bufferIndirect\n\t\t\t\t\t\t\t\t\t, bufferOffsetIndirect\n\t\t\t\t\t\t\t\t\t, numDrawIndirect\n\t\t\t\t\t\t\t\t\t, BGFX_CONFIG_DRAW_INDIRECT_STRIDE\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvkCmdDrawIndexed(\n\t\t\t\t\t\t\t\t  m_commandBuffer\n\t\t\t\t\t\t\t\t, numIndices\n\t\t\t\t\t\t\t\t, draw.m_numInstances\n\t\t\t\t\t\t\t\t, draw.m_startIndex\n\t\t\t\t\t\t\t\t, 0\n\t\t\t\t\t\t\t\t, 0\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tuint32_t numPrimsRendered  = numPrimsSubmitted*draw.m_numInstances;\n\n\t\t\t\t\tstatsNumPrimsSubmitted[primIndex] += numPrimsSubmitted;\n\t\t\t\t\tstatsNumPrimsRendered[primIndex]  += numPrimsRendered;\n\t\t\t\t\tstatsNumInstances[primIndex]      += draw.m_numInstances;\n\t\t\t\t\tstatsNumIndices                   += numIndices;\n\n\t\t\t\t\tif (hasOcclusionQuery)\n\t\t\t\t\t{\n\t\t\t\t\t\tm_occlusionQuery.end();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (beginRenderPass)\n\t\t\t{\n\t\t\t\tvkCmdEndRenderPass(m_commandBuffer);\n\t\t\t\tbeginRenderPass = false;\n\t\t\t}\n\n\t\t\tif (wasCompute)\n\t\t\t{\n\t\t\t\tsetViewType(view, "C");\n\t\t\t\tBGFX_VK_PROFILER_END();\n\t\t\t\tBGFX_VK_PROFILER_BEGIN(view, kColorCompute);\n\t\t\t}\n\n\t\t\tsubmitBlit(bs, BGFX_CONFIG_MAX_VIEWS);\n\n\t\t\tif (0 < _render->m_numRenderItems)\n\t\t\t{\n\t\t\t\tcaptureElapsed = -bx::getHPCounter();\n\t\t\t\tcapture();\n\t\t\t\tcaptureElapsed += bx::getHPCounter();\n\n\t\t\t\tprofiler.end();\n\t\t\t}\n\t\t}\n\n\t\tBGFX_VK_PROFILER_END();\n\n\t\tint64_t timeEnd = bx::getHPCounter();\n\t\tint64_t frameTime = timeEnd - timeBegin;\n\n\t\tstatic int64_t min = frameTime;\n\t\tstatic int64_t max = frameTime;\n\t\tmin = bx::min<int64_t>(min, frameTime);\n\t\tmax = bx::max<int64_t>(max, frameTime);\n\n\t\tstatic uint32_t maxGpuLatency = 0;\n\t\tstatic double   maxGpuElapsed = 0.0f;\n\t\tdouble elapsedGpuMs = 0.0;\n\n\t\tstatic int64_t presentMin = m_presentElapsed;\n\t\tstatic int64_t presentMax = m_presentElapsed;\n\t\tpresentMin = bx::min<int64_t>(presentMin, m_presentElapsed);\n\t\tpresentMax = bx::max<int64_t>(presentMax, m_presentElapsed);\n\n\t\tif (UINT32_MAX != frameQueryIdx)\n\t\t{\n\t\t\tm_gpuTimer.end(frameQueryIdx);\n\n\t\t\tconst TimerQueryVK::Result& result = m_gpuTimer.m_result[BGFX_CONFIG_MAX_VIEWS];\n\t\t\tdouble toGpuMs = 1000.0 / double(m_gpuTimer.m_frequency);\n\t\t\telapsedGpuMs   = (result.m_end - result.m_begin) * toGpuMs;\n\t\t\tmaxGpuElapsed  = elapsedGpuMs > maxGpuElapsed ? elapsedGpuMs : maxGpuElapsed;\n\n\t\t\tmaxGpuLatency = bx::uint32_imax(maxGpuLatency, result.m_pending-1);\n\t\t}\n\n\t\tmaxGpuLatency = bx::uint32_imax(maxGpuLatency, m_gpuTimer.m_control.available()-1);\n\n\t\tconst int64_t timerFreq = bx::getHPFrequency();\n\n\t\tVkPhysicalDeviceMemoryBudgetPropertiesEXT dmbp;\n\t\tdmbp.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;\n\t\tdmbp.pNext = NULL;\n\n\t\tint64_t gpuMemoryAvailable = -INT64_MAX;\n\t\tint64_t gpuMemoryUsed      = -INT64_MAX;\n\n\t\tif (s_extension[Extension::EXT_memory_budget].m_supported)\n\t\t{\n\t\t\tVkPhysicalDeviceMemoryProperties2 pdmp2;\n\t\t\tpdmp2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;\n\t\t\tpdmp2.pNext = &dmbp;\n\n\t\t\tvkGetPhysicalDeviceMemoryProperties2KHR(m_physicalDevice, &pdmp2);\n\n\t\t\tgpuMemoryAvailable = 0;\n\t\t\tgpuMemoryUsed      = 0;\n\n\t\t\tfor (uint32_t ii = 0; ii < m_memoryProperties.memoryHeapCount; ++ii)\n\t\t\t{\n\t\t\t\tif (!!(m_memoryProperties.memoryHeaps[ii].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) )\n\t\t\t\t{\n\t\t\t\t\tgpuMemoryAvailable += dmbp.heapBudget[ii];\n\t\t\t\t\tgpuMemoryUsed += dmbp.heapUsage[ii];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tStats& perfStats = _render->m_perfStats;\n\t\tperfStats.cpuTimeBegin  = timeBegin;\n\t\tperfStats.cpuTimeEnd    = timeEnd;\n\t\tperfStats.cpuTimerFreq  = timerFreq;\n\t\tconst TimerQueryVK::Result& result = m_gpuTimer.m_result[BGFX_CONFIG_MAX_VIEWS];\n\t\tperfStats.gpuTimeBegin  = result.m_begin;\n\t\tperfStats.gpuTimeEnd    = result.m_end;\n\t\tperfStats.gpuTimerFreq  = m_gpuTimer.m_frequency;\n\t\tperfStats.numDraw       = statsKeyType[0];\n\t\tperfStats.numCompute    = statsKeyType[1];\n\t\tperfStats.numBlit       = _render->m_numBlitItems;\n\t\tperfStats.maxGpuLatency = maxGpuLatency;\n\t\tperfStats.gpuFrameNum   = result.m_frameNum;\n\t\tbx::memCopy(perfStats.numPrims, statsNumPrimsRendered, sizeof(perfStats.numPrims) );\n\t\tperfStats.gpuMemoryMax  = gpuMemoryAvailable;\n\t\tperfStats.gpuMemoryUsed = gpuMemoryUsed;\n\n\t\tif (_render->m_debug & (BGFX_DEBUG_IFH|BGFX_DEBUG_STATS) )\n\t\t{\n\t\t\tBGFX_VK_PROFILER_BEGIN_LITERAL("debugstats", kColorFrame);\n\n\t\t\tTextVideoMem& tvm = m_textVideoMem;\n\n\t\t\tstatic int64_t next = timeEnd;\n\n\t\t\tif (timeEnd >= next)\n\t\t\t{\n\t\t\t\tnext = timeEnd + timerFreq;\n\n\t\t\t\tdouble freq = double(timerFreq);\n\t\t\t\tdouble toMs = 1000.0 / freq;\n\n\t\t\t\ttvm.clear();\n\t\t\t\tuint16_t pos = 0;\n\t\t\t\ttvm.printf(0, pos++, BGFX_CONFIG_DEBUG ? 0x8c : 0x8f\n\t\t\t\t\t, " %s / " BX_COMPILER_NAME\n\t\t\t\t\t  " / " BX_CPU_NAME\n\t\t\t\t\t  " / " BX_ARCH_NAME\n\t\t\t\t\t  " / " BX_PLATFORM_NAME\n\t\t\t\t\t  " / Version 1.%d.%d (commit: " BGFX_REV_SHA1 ")"\n\t\t\t\t\t, getRendererName()\n\t\t\t\t\t, BGFX_API_VERSION\n\t\t\t\t\t, BGFX_REV_NUMBER\n\t\t\t\t\t);\n\n\t\t\t\tconst VkPhysicalDeviceProperties& pdp = m_deviceProperties;\n\t\t\t\ttvm.printf(0, pos++, 0x8f, " Device: %s (%s)"\n\t\t\t\t\t, pdp.deviceName\n\t\t\t\t\t, getName(pdp.deviceType)\n\t\t\t\t\t);\n\n\t\t\t\tif (0 <= gpuMemoryAvailable && 0 <= gpuMemoryUsed)\n\t\t\t\t{\n\t\t\t\t\tfor (uint32_t ii = 0; ii < m_memoryProperties.memoryHeapCount; ++ii)\n\t\t\t\t\t{\n\t\t\t\t\t\tchar budget[16];\n\t\t\t\t\t\tbx::prettify(budget, BX_COUNTOF(budget), dmbp.heapBudget[ii]);\n\n\t\t\t\t\t\tchar usage[16];\n\t\t\t\t\t\tbx::prettify(usage, BX_COUNTOF(usage), dmbp.heapUsage[ii]);\n\n\t\t\t\t\t\tconst bool local = (!!(m_memoryProperties.memoryHeaps[ii].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) );\n\n\t\t\t\t\t\ttvm.printf(0, pos++, 0x8f, " Memory %d %s - Budget: %12s, Usage: %12s"\n\t\t\t\t\t\t\t, ii\n\t\t\t\t\t\t\t, local ? "(local)    " : "(non-local)"\n\t\t\t\t\t\t\t, budget\n\t\t\t\t\t\t\t, usage\n\t\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tpos = 10;\n\t\t\t\ttvm.printf(10, pos++, 0x8b, "       Frame: % 7.3f, % 7.3f \\x1f, % 7.3f \\x1e [ms] / % 6.2f FPS "\n\t\t\t\t\t, double(frameTime)*toMs\n\t\t\t\t\t, double(min)*toMs\n\t\t\t\t\t, double(max)*toMs\n\t\t\t\t\t, freq/frameTime\n\t\t\t\t\t);\n\t\t\t\ttvm.printf(10, pos++, 0x8b, "     Present: % 7.3f, % 7.3f \\x1f, % 7.3f \\x1e [ms] "\n\t\t\t\t\t, double(m_presentElapsed)*toMs\n\t\t\t\t\t, double(presentMin)*toMs\n\t\t\t\t\t, double(presentMax)*toMs\n\t\t\t\t\t);\n\n\t\t\t\tconst uint32_t msaa = (m_resolution.reset&BGFX_RESET_MSAA_MASK)>>BGFX_RESET_MSAA_SHIFT;\n\t\t\t\ttvm.printf(10, pos++, 0x8b, " Reset flags: [%c] vsync, [%c] MSAAx%d, [%c] MaxAnisotropy "\n\t\t\t\t\t, !!(m_resolution.reset&BGFX_RESET_VSYNC) ? \'\\xfe\' : \' \'\n\t\t\t\t\t, 0 != msaa ? \'\\xfe\' : \' \'\n\t\t\t\t\t, 1<<msaa\n\t\t\t\t\t, !!(m_resolution.reset&BGFX_RESET_MAXANISOTROPY) ? \'\\xfe\' : \' \'\n\t\t\t\t\t);\n\n\t\t\t\tdouble elapsedCpuMs = double(frameTime)*toMs;\n\t\t\t\ttvm.printf(10, pos++, 0x8b, "   Submitted: %5d (draw %5d, compute %4d) / CPU %7.4f [ms] "\n\t\t\t\t\t, _render->m_numRenderItems\n\t\t\t\t\t, statsKeyType[0]\n\t\t\t\t\t, statsKeyType[1]\n\t\t\t\t\t, elapsedCpuMs\n\t\t\t\t\t);\n\n\t\t\t\tfor (uint32_t ii = 0; ii < Topology::Count; ++ii)\n\t\t\t\t{\n\t\t\t\t\ttvm.printf(10, pos++, 0x8b, "   %9s: %7d (#inst: %5d), submitted: %7d "\n\t\t\t\t\t\t, getName(Topology::Enum(ii) )\n\t\t\t\t\t\t, statsNumPrimsRendered[ii]\n\t\t\t\t\t\t, statsNumInstances[ii]\n\t\t\t\t\t\t, statsNumPrimsSubmitted[ii]\n\t\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (NULL != m_renderDocDll)\n\t\t\t\t{\n\t\t\t\t\ttvm.printf(tvm.m_width-27, 0, 0x4f, " [F11 - RenderDoc capture] ");\n\t\t\t\t}\n\n\t\t\t\ttvm.printf(10, pos++, 0x8b, "      Indices: %7d ", statsNumIndices);\n//\t\t\t\ttvm.printf(10, pos++, 0x8b, " Uniform size: %7d, Max: %7d ", _render->m_uniformEnd, _render->m_uniformMax);\n\t\t\t\ttvm.printf(10, pos++, 0x8b, "     DVB size: %7d ", _render->m_vboffset);\n\t\t\t\ttvm.printf(10, pos++, 0x8b, "     DIB size: %7d ", _render->m_iboffset);\n\n\t\t\t\tpos++;\n\t\t\t\ttvm.printf(10, pos++, 0x8b, " Occlusion queries: %3d ", m_occlusionQuery.m_control.available() );\n\n\t\t\t\tpos++;\n\t\t\t\ttvm.printf(10, pos++, 0x8b, " State cache:             ");\n\t\t\t\ttvm.printf(10, pos++, 0x8b, " PSO    | DSL    |  DS    ");\n\t\t\t\ttvm.printf(10, pos++, 0x8b, " %6d | %6d | %6d "\n\t\t\t\t\t, m_pipelineStateCache.getCount()\n\t\t\t\t\t, m_descriptorSetLayoutCache.getCount()\n\t\t\t\t\t, descriptorSetCount\n\t\t\t\t\t);\n\t\t\t\tpos++;\n\n\t\t\t\tdouble captureMs = double(captureElapsed)*toMs;\n\t\t\t\ttvm.printf(10, pos++, 0x8b, "     Capture: %7.4f [ms] ", captureMs);\n\n\t\t\t\tuint8_t attr[2] = { 0x8c, 0x8a };\n\t\t\t\tuint8_t attrIndex = _render->m_waitSubmit < _render->m_waitRender;\n\n\t\t\t\ttvm.printf(10, pos++, attr[attrIndex&1], " Submit wait: %7.4f [ms] ", _render->m_waitSubmit*toMs);\n\t\t\t\ttvm.printf(10, pos++, attr[(attrIndex+1)&1], " Render wait: %7.4f [ms] ", _render->m_waitRender*toMs);\n\n\t\t\t\tmin = frameTime;\n\t\t\t\tmax = frameTime;\n\t\t\t\tpresentMin = m_presentElapsed;\n\t\t\t\tpresentMax = m_presentElapsed;\n\t\t\t}\n\n\t\t\tblit(this, _textVideoMemBlitter, tvm);\n\n\t\t\tBGFX_VK_PROFILER_END();\n\t\t}\n\t\telse if (_render->m_debug & BGFX_DEBUG_TEXT)\n\t\t{\n\t\t\tBGFX_VK_PROFILER_BEGIN_LITERAL("debugtext", kColorFrame);\n\n\t\t\tblit(this, _textVideoMemBlitter, _render->m_textVideoMem);\n\n\t\t\tBGFX_VK_PROFILER_END();\n\t\t}\n\n\t\tm_presentElapsed = 0;\n\n\t\tscratchBuffer.flush();\n\n\t\tfor (uint16_t ii = 0; ii < m_numWindows; ++ii)\n\t\t{\n\t\t\tFrameBufferVK& fb = isValid(m_windows[ii])\n\t\t\t\t? m_frameBuffers[m_windows[ii].idx]\n\t\t\t\t: m_backBuffer\n\t\t\t\t;\n\n\t\t\tif (fb.m_needPresent)\n\t\t\t{\n\t\t\t\tfb.resolve();\n\n\t\t\t\tfb.m_swapChain.transitionImage(m_commandBuffer);\n\n\t\t\t\tm_cmd.addWaitSemaphore(fb.m_swapChain.m_lastImageAcquiredSemaphore, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);\n\t\t\t\tm_cmd.addSignalSemaphore(fb.m_swapChain.m_lastImageRenderedSemaphore);\n\t\t\t\tfb.m_swapChain.m_lastImageAcquiredSemaphore = VK_NULL_HANDLE;\n\n\t\t\t\tfb.m_swapChain.m_backBufferFence[fb.m_swapChain.m_backBufferColorIdx] = m_cmd.m_currentFence;\n\t\t\t}\n\t\t}\n\n\t\tkick();\n\t}\n\n} /* namespace vk */ } // namespace bgfx\n\n#else\n\nnamespace bgfx { namespace vk\n{\n\tRendererContextI* rendererCreate(const Init& _init)\n\t{\n\t\tBX_UNUSED(_init);\n\t\treturn NULL;\n\t}\n\n\tvoid rendererDestroy()\n\t{\n\t}\n} /* namespace vk */ } // namespace bgfx\n\n#endif // BGFX_CONFIG_RENDERER_VULKAN\n', 'message': 'Vulkan cleanup and minor bug fixes (#2129)\n\n', 'author_name': 'GitHub', 'lang': 'cpp'}