diff --git a/.gitignore b/.gitignore index aec2911..4feb848 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ xcuserdata/ build/ CMakeCache.txt cmake.check_cache +moonbuild/ +moonbuildMac/ +moonbuildWin64/ +dist/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 7095da0..fd06c90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,9 +7,9 @@ project(Refresh C) option(BUILD_SHARED_LIBS "Build shared library" ON) # Version -SET(LIB_MAJOR_VERSION "1") -SET(LIB_MINOR_VERSION "15") -SET(LIB_REVISION "4") +SET(LIB_MAJOR_VERSION "2") +SET(LIB_MINOR_VERSION "0") +SET(LIB_REVISION "0") SET(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") # Build Type @@ -38,21 +38,38 @@ endif() # Defines add_definitions( - -DREFRESH_DRIVER_VULKAN + -DREFRESH_VULKAN ) +if (WIN32) + add_definitions( + -DREFRESH_D3D11 + ) +endif() + +if (APPLE) + add_definitions( + -DREFRESH_METAL + ) +endif() + # Source lists add_library(Refresh # Public Headers include/Refresh.h - include/Refresh_Image.h + include/Refresh_image.h # Internal Headers - src/Refresh_Driver.h - src/Refresh_Driver_Vulkan_vkfuncs.h + src/Refresh_driver.h + src/Refresh_spirv_c.h + src/spirv_cross_c.h + src/vulkan/Refresh_vulkan_vkfuncs.h # Source Files src/Refresh.c - src/Refresh_Driver_Vulkan.c - src/Refresh_Image.c + src/Refresh_spirv.c + src/d3d11/Refresh_d3d11.c + src/d3d11/Refresh_d3d11_d3dcompiler.c + src/vulkan/Refresh_vulkan.c + src/Refresh_image.c ) # Build flags diff --git a/README.md b/README.md index 1fc21ef..be0f9f0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -[![Build Status](https://gitea.drone.moonside.games/api/badges/MoonsideGames/Refresh/status.svg)](https://gitea.drone.moonside.games/MoonsideGames/Refresh) - -This is Refresh, an XNA-inspired 3D graphics library with modern capabilities. +This is Refresh, a cross-platform hardware-accelerated graphics library with modern capabilities. License ------- @@ -8,16 +6,20 @@ Refresh is licensed under the zlib license. See LICENSE for details. About Refresh ------------- -Refresh is directly inspired by FNA3D and intended to be a replacement for XNA's Graphics namespace. -XNA 4.0 is a powerful API, but its shader system is outdated and certain restrictions are awkward to handle in a modern context. -In the way that XNA was "one step above" DX9, Refresh intends to be "one step above" Vulkan. It should map nicely to modern graphics APIs. -Refresh will initially have a Vulkan runtime implementation. Support for other APIs like DX12 may come later. -For shaders, we consume SPIR-V bytecode. +The contemporary graphics landscape is daunting. Older and easier-to-learn APIs like OpenGL are being replaced by explicit APIs like Vulkan. +These newer APIs have many appealing features, like threading support and bundled state management, +but using them in practice requires complex management of memory and synchronization. +What's a developer to do? + +Refresh is a middle ground between these two extremes. The API aims for the simplicity of OpenGL combined with the power of Vulkan. +Refresh supports all desktop platforms. Vulkan and D3D11 backends are complete, and Metal support is coming soon. +Refresh supports portable shaders through SPIRV-Cross, but it also allows you to provide backend-specific shader formats, so you can use any shader toolchain you like. Dependencies ------------ Refresh depends on SDL2 for portability. Refresh never explicitly uses the C runtime. +SPIRV-Cross is dynamically linked as an optional dependency. Building Refresh ---------------- @@ -34,4 +36,4 @@ Want to contribute? ------------------- Issues can be reported and patches contributed via Github: -https://github.com/thatcosmonaut/Refresh +https://github.com/MoonsideGames/Refresh diff --git a/include/Refresh.h b/include/Refresh.h index 9c173bb..8583e68 100644 --- a/include/Refresh.h +++ b/include/Refresh.h @@ -1,6 +1,6 @@ -/* Refresh - XNA-inspired 3D Graphics Library with modern capabilities +/* Refresh - a cross-platform hardware-accelerated graphics library with modern capabilities * - * Copyright (c) 2020 Evan Hemsley + * Copyright (c) 2020-2024 Evan Hemsley * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from @@ -24,11 +24,12 @@ * */ -#include - #ifndef REFRESH_H #define REFRESH_H +#include "SDL.h" +#include + #ifdef _WIN32 #define REFRESHAPI __declspec(dllexport) #define REFRESHCALL __cdecl @@ -46,45 +47,26 @@ #endif /* __GNUC__ */ #endif /* REFRESHNAMELESS */ -#include - #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -/* Version API */ - -#define REFRESH_MAJOR_VERSION 1 -#define REFRESH_MINOR_VERSION 15 -#define REFRESH_PATCH_VERSION 4 - -#define REFRESH_COMPILED_VERSION ( \ - (REFRESH_MAJOR_VERSION * 100 * 100) + \ - (REFRESH_MINOR_VERSION * 100) + \ - (REFRESH_PATCH_VERSION) \ -) - -REFRESHAPI uint32_t Refresh_LinkedVersion(void); - /* Type Declarations */ typedef struct Refresh_Device Refresh_Device; typedef struct Refresh_Buffer Refresh_Buffer; +typedef struct Refresh_TransferBuffer Refresh_TransferBuffer; typedef struct Refresh_Texture Refresh_Texture; typedef struct Refresh_Sampler Refresh_Sampler; -typedef struct Refresh_ShaderModule Refresh_ShaderModule; +typedef struct Refresh_Shader Refresh_Shader; typedef struct Refresh_ComputePipeline Refresh_ComputePipeline; typedef struct Refresh_GraphicsPipeline Refresh_GraphicsPipeline; typedef struct Refresh_CommandBuffer Refresh_CommandBuffer; +typedef struct Refresh_RenderPass Refresh_RenderPass; +typedef struct Refresh_ComputePass Refresh_ComputePass; +typedef struct Refresh_CopyPass Refresh_CopyPass; typedef struct Refresh_Fence Refresh_Fence; - -typedef enum Refresh_PresentMode -{ - REFRESH_PRESENTMODE_IMMEDIATE, - REFRESH_PRESENTMODE_MAILBOX, - REFRESH_PRESENTMODE_FIFO, - REFRESH_PRESENTMODE_FIFO_RELAXED -} Refresh_PresentMode; +typedef struct Refresh_OcclusionQuery Refresh_OcclusionQuery; typedef enum Refresh_PrimitiveType { @@ -123,9 +105,11 @@ typedef enum Refresh_TextureFormat REFRESH_TEXTUREFORMAT_A1R5G5B5, REFRESH_TEXTUREFORMAT_B4G4R4A4, REFRESH_TEXTUREFORMAT_A2R10G10B10, + REFRESH_TEXTUREFORMAT_A2B10G10R10, REFRESH_TEXTUREFORMAT_R16G16, REFRESH_TEXTUREFORMAT_R16G16B16A16, REFRESH_TEXTUREFORMAT_R8, + REFRESH_TEXTUREFORMAT_A8, /* Compressed Unsigned Normalized Float Color Formats */ REFRESH_TEXTUREFORMAT_BC1, REFRESH_TEXTUREFORMAT_BC2, @@ -148,22 +132,38 @@ typedef enum Refresh_TextureFormat REFRESH_TEXTUREFORMAT_R16_UINT, REFRESH_TEXTUREFORMAT_R16G16_UINT, REFRESH_TEXTUREFORMAT_R16G16B16A16_UINT, + /* SRGB Color Formats */ + REFRESH_TEXTUREFORMAT_R8G8B8A8_SRGB, + REFRESH_TEXTUREFORMAT_B8G8R8A8_SRGB, + /* Compressed SRGB Color Formats */ + REFRESH_TEXTUREFORMAT_BC3_SRGB, + REFRESH_TEXTUREFORMAT_BC7_SRGB, /* Depth Formats */ REFRESH_TEXTUREFORMAT_D16_UNORM, + REFRESH_TEXTUREFORMAT_D24_UNORM, REFRESH_TEXTUREFORMAT_D32_SFLOAT, - REFRESH_TEXTUREFORMAT_D16_UNORM_S8_UINT, + REFRESH_TEXTUREFORMAT_D24_UNORM_S8_UINT, REFRESH_TEXTUREFORMAT_D32_SFLOAT_S8_UINT } Refresh_TextureFormat; typedef enum Refresh_TextureUsageFlagBits { - REFRESH_TEXTUREUSAGE_SAMPLER_BIT = 0x00000001, - REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT = 0x00000002, - REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT = 0x00000004, - REFRESH_TEXTUREUSAGE_COMPUTE_BIT = 0X00000008 + REFRESH_TEXTUREUSAGE_SAMPLER_BIT = 0x00000001, + REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT = 0x00000002, + REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT = 0x00000004, + REFRESH_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT = 0x00000008, + REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT = 0x00000020, + REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT = 0x00000040 } Refresh_TextureUsageFlagBits; -typedef uint32_t Refresh_TextureUsageFlags; +typedef Uint32 Refresh_TextureUsageFlags; + +typedef enum Refresh_TextureType +{ + REFRESH_TEXTURETYPE_2D, + REFRESH_TEXTURETYPE_3D, + REFRESH_TEXTURETYPE_CUBE, +} Refresh_TextureType; typedef enum Refresh_SampleCount { @@ -187,11 +187,39 @@ typedef enum Refresh_BufferUsageFlagBits { REFRESH_BUFFERUSAGE_VERTEX_BIT = 0x00000001, REFRESH_BUFFERUSAGE_INDEX_BIT = 0x00000002, - REFRESH_BUFFERUSAGE_COMPUTE_BIT = 0x00000004, - REFRESH_BUFFERUSAGE_INDIRECT_BIT = 0x00000008 + REFRESH_BUFFERUSAGE_INDIRECT_BIT = 0x00000004, + REFRESH_BUFFERUSAGE_GRAPHICS_STORAGE_READ_BIT = 0x00000008, + REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_READ_BIT = 0x00000020, + REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_WRITE_BIT = 0x00000040 } Refresh_BufferUsageFlagBits; -typedef uint32_t Refresh_BufferUsageFlags; +typedef Uint32 Refresh_BufferUsageFlags; + +typedef enum Refresh_TransferBufferMapFlagBits +{ + REFRESH_TRANSFER_MAP_READ = 0x00000001, + REFRESH_TRANSFER_MAP_WRITE = 0x00000002 +} Refresh_TransferBufferMapFlagBits; + +typedef Uint32 Refresh_TransferBufferMapFlags; + +typedef enum Refresh_ShaderStage +{ + REFRESH_SHADERSTAGE_VERTEX, + REFRESH_SHADERSTAGE_FRAGMENT, + REFRESH_SHADERSTAGE_COMPUTE +} Refresh_ShaderStage; + +typedef enum Refresh_ShaderFormat +{ + REFRESH_SHADERFORMAT_INVALID, + REFRESH_SHADERFORMAT_SPIRV, /* Vulkan */ + REFRESH_SHADERFORMAT_DXBC, /* D3D11, D3D12 */ + REFRESH_SHADERFORMAT_DXIL, /* D3D12 */ + REFRESH_SHADERFORMAT_MSL, /* Metal */ + REFRESH_SHADERFORMAT_METALLIB, /* Metal */ + REFRESH_SHADERFORMAT_SECRET /* NDA'd platforms */ +} Refresh_ShaderFormat; typedef enum Refresh_VertexElementFormat { @@ -293,7 +321,7 @@ typedef enum Refresh_ColorComponentFlagBits REFRESH_COLORCOMPONENT_A_BIT = 0x00000008 } Refresh_ColorComponentFlagBits; -typedef uint32_t Refresh_ColorComponentFlags; +typedef Uint32 Refresh_ColorComponentFlags; typedef enum Refresh_Filter { @@ -326,37 +354,87 @@ typedef enum Refresh_BorderColor REFRESH_BORDERCOLOR_INT_OPAQUE_WHITE } Refresh_BorderColor; -typedef enum Refresh_Backend +typedef enum Refresh_TransferUsage { - REFRESH_BACKEND_DONTCARE, - REFRESH_BACKEND_VULKAN, - REFRESH_BACKEND_PS5, - REFRESH_BACKEND_INVALID -} Refresh_Backend; + REFRESH_TRANSFERUSAGE_BUFFER, + REFRESH_TRANSFERUSAGE_TEXTURE +} Refresh_TransferUsage; + +/* + * VSYNC: + * Waits for vblank before presenting. + * If there is a pending image to present, the new image is enqueued for presentation. + * Disallows tearing at the cost of visual latency. + * When using this present mode, AcquireSwapchainTexture will block if too many frames are in flight. + * IMMEDIATE: + * Immediately presents. + * Lowest latency option, but tearing may occur. + * When using this mode, AcquireSwapchainTexture will return NULL if too many frames are in flight. + * MAILBOX: + * Waits for vblank before presenting. No tearing is possible. + * If there is a pending image to present, the pending image is replaced by the new image. + * Similar to VSYNC, but with reduced visual latency. + * When using this mode, AcquireSwapchainTexture will return NULL if too many frames are in flight. + */ +typedef enum Refresh_PresentMode +{ + REFRESH_PRESENTMODE_VSYNC, + REFRESH_PRESENTMODE_IMMEDIATE, + REFRESH_PRESENTMODE_MAILBOX +} Refresh_PresentMode; + +/* + * SDR: + * B8G8R8A8 or R8G8B8A8 swapchain. Pixel values are in nonlinear sRGB encoding. Blends raw pixel values. + * SDR_LINEAR: + * B8G8R8A8_SRGB or R8G8B8A8_SRGB swapchain. Pixel values are in nonlinear sRGB encoding. Blends in linear space. + * HDR_EXTENDED_LINEAR: + * R16G16B16A16_SFLOAT swapchain. Pixel values are in extended linear encoding. Blends in linear space. + * HDR10_ST2048: + * A2R10G10B10 or A2B10G10R10 swapchain. Pixel values are in PQ ST2048 encoding. Blends raw pixel values. (TODO: verify this) + */ +typedef enum Refresh_SwapchainComposition +{ + REFRESH_SWAPCHAINCOMPOSITION_SDR, + REFRESH_SWAPCHAINCOMPOSITION_SDR_LINEAR, + REFRESH_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR, + REFRESH_SWAPCHAINCOMPOSITION_HDR10_ST2048 +} Refresh_SwapchainComposition; + +typedef enum Refresh_BackendBits +{ + REFRESH_BACKEND_INVALID = 0, + REFRESH_BACKEND_VULKAN = 0x0000000000000001, + REFRESH_BACKEND_D3D11 = 0x0000000000000002, + REFRESH_BACKEND_METAL = 0x0000000000000004, + REFRESH_BACKEND_ALL = (REFRESH_BACKEND_VULKAN | REFRESH_BACKEND_D3D11 | REFRESH_BACKEND_METAL) +} Refresh_BackendBits; + +typedef Uint64 Refresh_Backend; /* Structures */ typedef struct Refresh_DepthStencilValue { float depth; - uint32_t stencil; + Uint32 stencil; } Refresh_DepthStencilValue; typedef struct Refresh_Rect { - int32_t x; - int32_t y; - int32_t w; - int32_t h; + Sint32 x; + Sint32 y; + Sint32 w; + Sint32 h; } Refresh_Rect; -typedef struct Refresh_Vec4 +typedef struct Refresh_Color { - float x; - float y; - float z; - float w; -} Refresh_Vec4; + float r; + float g; + float b; + float a; +} Refresh_Color; typedef struct Refresh_Viewport { @@ -371,23 +449,55 @@ typedef struct Refresh_Viewport typedef struct Refresh_TextureSlice { Refresh_Texture *texture; - Refresh_Rect rectangle; - uint32_t depth; /* 0 unless 3D */ - uint32_t layer; /* 0 unless cube */ - uint32_t level; + Uint32 mipLevel; + Uint32 layer; } Refresh_TextureSlice; +typedef struct Refresh_TextureRegion +{ + Refresh_TextureSlice textureSlice; + Uint32 x; + Uint32 y; + Uint32 z; + Uint32 w; + Uint32 h; + Uint32 d; +} Refresh_TextureRegion; + +typedef struct Refresh_BufferImageCopy +{ + Uint32 bufferOffset; + Uint32 bufferStride; /* number of pixels from one row to the next */ + Uint32 bufferImageHeight; /* number of rows from one layer/depth-slice to the next */ +} Refresh_BufferImageCopy; + +typedef struct Refresh_BufferCopy +{ + Uint32 srcOffset; + Uint32 dstOffset; + Uint32 size; +} Refresh_BufferCopy; + typedef struct Refresh_IndirectDrawCommand { - uint32_t vertexCount; - uint32_t instanceCount; - uint32_t firstVertex; - uint32_t firstInstance; + Uint32 vertexCount; /* number of vertices to draw */ + Uint32 instanceCount; /* number of instances to draw */ + Uint32 firstVertex; /* index of the first vertex to draw */ + Uint32 firstInstance; /* ID of the first instance to draw */ } Refresh_IndirectDrawCommand; +typedef struct Refresh_IndexedIndirectDrawCommand +{ + Uint32 indexCount; /* number of vertices to draw */ + Uint32 instanceCount; /* number of instances to draw */ + Uint32 firstIndex; /* base index within the index buffer */ + Uint32 vertexOffset; /* value added to vertex index before indexing into the vertex buffer */ + Uint32 firstInstance; /* ID of the first instance to draw */ +} Refresh_IndexedIndirectDrawCommand; + /* State structures */ -typedef struct Refresh_SamplerStateCreateInfo +typedef struct Refresh_SamplerCreateInfo { Refresh_Filter minFilter; Refresh_Filter magFilter; @@ -396,36 +506,37 @@ typedef struct Refresh_SamplerStateCreateInfo Refresh_SamplerAddressMode addressModeV; Refresh_SamplerAddressMode addressModeW; float mipLodBias; - uint8_t anisotropyEnable; + SDL_bool anisotropyEnable; float maxAnisotropy; - uint8_t compareEnable; + SDL_bool compareEnable; Refresh_CompareOp compareOp; float minLod; float maxLod; Refresh_BorderColor borderColor; -} Refresh_SamplerStateCreateInfo; +} Refresh_SamplerCreateInfo; typedef struct Refresh_VertexBinding { - uint32_t binding; - uint32_t stride; + Uint32 binding; + Uint32 stride; Refresh_VertexInputRate inputRate; + Uint32 stepRate; } Refresh_VertexBinding; typedef struct Refresh_VertexAttribute { - uint32_t location; - uint32_t binding; + Uint32 location; + Uint32 binding; Refresh_VertexElementFormat format; - uint32_t offset; + Uint32 offset; } Refresh_VertexAttribute; typedef struct Refresh_VertexInputState { const Refresh_VertexBinding *vertexBindings; - uint32_t vertexBindingCount; + Uint32 vertexBindingCount; const Refresh_VertexAttribute *vertexAttributes; - uint32_t vertexAttributeCount; + Uint32 vertexAttributeCount; } Refresh_VertexInputState; typedef struct Refresh_StencilOpState @@ -434,14 +545,11 @@ typedef struct Refresh_StencilOpState Refresh_StencilOp passOp; Refresh_StencilOp depthFailOp; Refresh_CompareOp compareOp; - uint32_t compareMask; - uint32_t writeMask; - uint32_t reference; } Refresh_StencilOpState; typedef struct Refresh_ColorAttachmentBlendState { - uint8_t blendEnable; + SDL_bool blendEnable; Refresh_BlendFactor srcColorBlendFactor; Refresh_BlendFactor dstColorBlendFactor; Refresh_BlendOp colorBlendOp; @@ -451,19 +559,23 @@ typedef struct Refresh_ColorAttachmentBlendState Refresh_ColorComponentFlags colorWriteMask; } Refresh_ColorAttachmentBlendState; -typedef struct Refresh_ShaderModuleCreateInfo +typedef struct Refresh_ShaderCreateInfo { size_t codeSize; - const uint32_t *byteCode; -} Refresh_ShaderModuleCreateInfo; + const Uint8 *code; + const char* entryPointName; + Refresh_ShaderStage stage; + Refresh_ShaderFormat format; +} Refresh_ShaderCreateInfo; typedef struct Refresh_TextureCreateInfo { - uint32_t width; - uint32_t height; - uint32_t depth; - uint8_t isCube; - uint32_t levelCount; + Uint32 width; + Uint32 height; + Uint32 depth; + SDL_bool isCube; + Uint32 layerCount; + Uint32 levelCount; Refresh_SampleCount sampleCount; Refresh_TextureFormat format; Refresh_TextureUsageFlags usageFlags; @@ -471,29 +583,12 @@ typedef struct Refresh_TextureCreateInfo /* Pipeline state structures */ -typedef struct Refresh_GraphicsShaderInfo -{ - Refresh_ShaderModule *shaderModule; - const char* entryPointName; - uint64_t uniformBufferSize; - uint32_t samplerBindingCount; -} Refresh_GraphicsShaderInfo; - -typedef struct Refresh_ComputeShaderInfo -{ - Refresh_ShaderModule* shaderModule; - const char* entryPointName; - uint64_t uniformBufferSize; - uint32_t bufferBindingCount; - uint32_t imageBindingCount; -} Refresh_ComputeShaderInfo; - typedef struct Refresh_RasterizerState { Refresh_FillMode fillMode; Refresh_CullMode cullMode; Refresh_FrontFace frontFace; - uint8_t depthBiasEnable; + SDL_bool depthBiasEnable; float depthBiasConstantFactor; float depthBiasClamp; float depthBiasSlopeFactor; @@ -502,18 +597,21 @@ typedef struct Refresh_RasterizerState typedef struct Refresh_MultisampleState { Refresh_SampleCount multisampleCount; - uint32_t sampleMask; + Uint32 sampleMask; } Refresh_MultisampleState; typedef struct Refresh_DepthStencilState { - uint8_t depthTestEnable; - uint8_t depthWriteEnable; + SDL_bool depthTestEnable; + SDL_bool depthWriteEnable; Refresh_CompareOp compareOp; - uint8_t depthBoundsTestEnable; - uint8_t stencilTestEnable; - Refresh_StencilOpState frontStencilState; + SDL_bool depthBoundsTestEnable; + SDL_bool stencilTestEnable; Refresh_StencilOpState backStencilState; + Refresh_StencilOpState frontStencilState; + Uint32 compareMask; + Uint32 writeMask; + Uint32 reference; float minDepthBounds; float maxDepthBounds; } Refresh_DepthStencilState; @@ -527,734 +625,1519 @@ typedef struct Refresh_ColorAttachmentDescription typedef struct Refresh_GraphicsPipelineAttachmentInfo { Refresh_ColorAttachmentDescription *colorAttachmentDescriptions; - uint32_t colorAttachmentCount; - uint8_t hasDepthStencilAttachment; + Uint32 colorAttachmentCount; + SDL_bool hasDepthStencilAttachment; Refresh_TextureFormat depthStencilFormat; } Refresh_GraphicsPipelineAttachmentInfo; +typedef struct Refresh_GraphicsPipelineResourceInfo +{ + Uint32 samplerCount; + Uint32 storageBufferCount; + Uint32 storageTextureCount; + Uint32 uniformBufferCount; +} Refresh_GraphicsPipelineResourceInfo; + typedef struct Refresh_GraphicsPipelineCreateInfo { - Refresh_GraphicsShaderInfo vertexShaderInfo; - Refresh_GraphicsShaderInfo fragmentShaderInfo; + Refresh_Shader *vertexShader; + Refresh_Shader *fragmentShader; Refresh_VertexInputState vertexInputState; Refresh_PrimitiveType primitiveType; Refresh_RasterizerState rasterizerState; Refresh_MultisampleState multisampleState; Refresh_DepthStencilState depthStencilState; Refresh_GraphicsPipelineAttachmentInfo attachmentInfo; + Refresh_GraphicsPipelineResourceInfo vertexResourceInfo; + Refresh_GraphicsPipelineResourceInfo fragmentResourceInfo; float blendConstants[4]; } Refresh_GraphicsPipelineCreateInfo; -/* Render pass structures */ +typedef struct Refresh_ComputePipelineResourceInfo +{ + Uint32 readOnlyStorageTextureCount; + Uint32 readOnlyStorageBufferCount; + Uint32 readWriteStorageTextureCount; + Uint32 readWriteStorageBufferCount; + Uint32 uniformBufferCount; +} Refresh_ComputePipelineResourceInfo; + +typedef struct Refresh_ComputePipelineCreateInfo +{ + Refresh_Shader *computeShader; + Refresh_ComputePipelineResourceInfo pipelineResourceInfo; +} Refresh_ComputePipelineCreateInfo; typedef struct Refresh_ColorAttachmentInfo { - Refresh_Texture *texture; /* We can't use TextureSlice because render passes take a single rectangle. */ - uint32_t depth; - uint32_t layer; - uint32_t level; - Refresh_Vec4 clearColor; /* Can be ignored by RenderPass */ + /* The texture slice that will be used as a color attachment by a render pass. */ + Refresh_TextureSlice textureSlice; + + /* Can be ignored by RenderPass if CLEAR is not used */ + Refresh_Color clearColor; + + /* Determines what is done with the texture slice at the beginning of the render pass. + * + * LOAD: + * Loads the data currently in the texture slice. + * + * CLEAR: + * Clears the texture slice to a single color. + * + * DONT_CARE: + * The driver will do whatever it wants with the texture slice memory. + * This is a good option if you know that every single pixel will be touched in the render pass. + */ Refresh_LoadOp loadOp; + + /* Determines what is done with the texture slice at the end of the render pass. + * + * STORE: + * Stores the results of the render pass in the texture slice. + * + * DONT_CARE: + * The driver will do whatever it wants with the texture slice memory. + * This is often a good option for depth/stencil textures. + */ Refresh_StoreOp storeOp; + + /* if SDL_TRUE, cycles the texture if the texture slice is bound and loadOp is not LOAD */ + SDL_bool cycle; } Refresh_ColorAttachmentInfo; typedef struct Refresh_DepthStencilAttachmentInfo { - Refresh_Texture *texture; /* We can't use TextureSlice because render passes take a single rectangle. */ - uint32_t depth; - uint32_t layer; - uint32_t level; - Refresh_DepthStencilValue depthStencilClearValue; /* Can be ignored by RenderPass */ + /* The texture slice that will be used as the depth stencil attachment by a render pass. */ + Refresh_TextureSlice textureSlice; + + /* Can be ignored by the render pass if CLEAR is not used */ + Refresh_DepthStencilValue depthStencilClearValue; + + /* Determines what is done with the depth values at the beginning of the render pass. + * + * LOAD: + * Loads the depth values currently in the texture slice. + * + * CLEAR: + * Clears the texture slice to a single depth. + * + * DONT_CARE: + * The driver will do whatever it wants with the memory. + * This is a good option if you know that every single pixel will be touched in the render pass. + */ Refresh_LoadOp loadOp; + + /* Determines what is done with the depth values at the end of the render pass. + * + * STORE: + * Stores the depth results in the texture slice. + * + * DONT_CARE: + * The driver will do whatever it wants with the texture slice memory. + * This is often a good option for depth/stencil textures. + */ Refresh_StoreOp storeOp; + + /* Determines what is done with the stencil values at the beginning of the render pass. + * + * LOAD: + * Loads the stencil values currently in the texture slice. + * + * CLEAR: + * Clears the texture slice to a single stencil value. + * + * DONT_CARE: + * The driver will do whatever it wants with the memory. + * This is a good option if you know that every single pixel will be touched in the render pass. + */ Refresh_LoadOp stencilLoadOp; + + /* Determines what is done with the stencil values at the end of the render pass. + * + * STORE: + * Stores the stencil results in the texture slice. + * + * DONT_CARE: + * The driver will do whatever it wants with the texture slice memory. + * This is often a good option for depth/stencil textures. + */ Refresh_StoreOp stencilStoreOp; + + /* if SDL_TRUE, cycles the texture if the texture slice is bound and any load ops are not LOAD */ + SDL_bool cycle; } Refresh_DepthStencilAttachmentInfo; +/* Binding structs */ + +typedef struct Refresh_BufferBinding +{ + Refresh_Buffer *buffer; + Uint32 offset; +} Refresh_BufferBinding; + +typedef struct Refresh_TextureSamplerBinding +{ + Refresh_Texture *texture; + Refresh_Sampler *sampler; +} Refresh_TextureSamplerBinding; + +typedef struct Refresh_StorageBufferReadWriteBinding +{ + Refresh_Buffer *buffer; + + /* if SDL_TRUE, cycles the buffer if it is bound. */ + SDL_bool cycle; +} Refresh_StorageBufferReadWriteBinding; + +typedef struct Refresh_StorageTextureReadWriteBinding +{ + Refresh_TextureSlice textureSlice; + + /* if SDL_TRUE, cycles the texture if the texture slice is bound. */ + SDL_bool cycle; +} Refresh_StorageTextureReadWriteBinding; + /* Functions */ -/* Logging */ - -typedef void (REFRESHCALL * Refresh_LogFunc)(const char *msg); - -/* Reroutes Refresh's logging to custom logging functions. - * - * info: Basic logs that might be useful to have stored for support. - * warn: Something went wrong, but it's really just annoying, not fatal. - * error: You better have this stored somewhere because it's crashing now! - */ -REFRESHAPI void Refresh_HookLogFunctions( - Refresh_LogFunc info, - Refresh_LogFunc warn, - Refresh_LogFunc error -); - -/* Backend selection */ - -/* Select the graphics API backend that Refresh should use. - * - * Note that Refresh is not required to select your preferred backend - * if it detects an incompatibility. - * - * Returns the backend that will actually be used, and fills in a window flag bitmask. - * This bitmask should be used to create all windows that the device claims. - * - * preferredBackend: The preferred backend that Refresh should select. - * flags: A pointer to a bitflag value that will be filled in with required SDL_WindowFlags masks. - */ -REFRESHAPI Refresh_Backend Refresh_SelectBackend(Refresh_Backend preferredBackend, uint32_t *flags); - /* Device */ -/* Create a rendering context for use on the calling thread. - * You MUST have called Refresh_SelectDriver prior to calling this function. +/** + * Creates a GPU context. * - * debugMode: Enable debug mode properties. + * Backends will first be checked for availability in order of bitflags passed using preferredBackends. If none of the backends are available, the remaining backends are checked as fallback renderers. + * + * Think of "preferred" backends as those that have pre-built shaders readily available - for example, you would set the REFRESH_BACKEND_VULKAN bit if your game includes SPIR-V shaders. If you generate shaders at runtime (i.e. via SDL_shader) and the library does _not_ provide you with a preferredBackends value, you should pass REFRESH_BACKEND_ALL so that updated versions of SDL can be aware of which backends the application was aware of at compile time. REFRESH_BACKEND_INVALID is an accepted value but is not recommended. + * + * \param preferredBackends a bitflag containing the renderers most recognized by the application + * \param debugMode enable debug mode properties and validations + * \returns a GPU context on success or NULL on failure + * + * \sa Refresh_SelectBackend + * \sa Refresh_DestroyDevice */ REFRESHAPI Refresh_Device* Refresh_CreateDevice( - uint8_t debugMode + Refresh_Backend preferredBackends, + SDL_bool debugMode ); -/* Destroys a rendering context previously returned by Refresh_CreateDevice. */ +/** + * Destroys a GPU context previously returned by Refresh_CreateDevice. + * + * \param device a GPU Context to destroy + * + * \sa Refresh_CreateDevice + */ REFRESHAPI void Refresh_DestroyDevice(Refresh_Device *device); -/* Drawing */ - -/* Draws data from vertex/index buffers with instancing enabled. +/** + * Returns the backend used to create this GPU context. * - * baseVertex: The starting offset to read from the vertex buffer. - * startIndex: The starting offset to read from the index buffer. - * primitiveCount: The number of primitives to draw. - * instanceCount: The number of instances that will be drawn. - * vertexParamOffset: The offset of the vertex shader param data. - * fragmentParamOffset: The offset of the fragment shader param data. - */ -REFRESHAPI void Refresh_DrawInstancedPrimitives( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - uint32_t baseVertex, - uint32_t startIndex, - uint32_t primitiveCount, - uint32_t instanceCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset -); - -/* Draws data from vertex/index buffers. + * \param device a GPU context to query + * \returns an Refresh_Backend value, or REFRESH_BACKEND_INVALID on error * - * baseVertex: The starting offset to read from the vertex buffer. - * startIndex: The starting offset to read from the index buffer. - * primitiveCount: The number of primitives to draw. - * vertexParamOffset: The offset of the vertex shader param data. - * fragmentParamOffset: The offset of the fragment shader param data. + * \sa Refresh_SelectBackend */ -REFRESHAPI void Refresh_DrawIndexedPrimitives( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - uint32_t baseVertex, - uint32_t startIndex, - uint32_t primitiveCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset -); - -/* Draws data from vertex buffers. - * - * vertexStart: The starting offset to read from the vertex buffer. - * primitiveCount: The number of primitives to draw. - * vertexParamOffset: The offset of the vertex shader param data. - * fragmentParamOffset: The offset of the fragment shader param data. - */ -REFRESHAPI void Refresh_DrawPrimitives( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - uint32_t vertexStart, - uint32_t primitiveCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset -); - -/* Similar to Refresh_DrawPrimitives, but draw parameters are set from a buffer. - * The buffer layout should match the layout of Refresh_IndirectDrawCommand. - * - * buffer: A buffer containing draw parameters. - * offsetInBytes: The offset to start reading from the draw buffer. - * drawCount: The number of draw parameter sets that should be read from the draw buffer. - * stride: The byte stride between sets of draw parameters. - * vertexParamOffset: The offset of the vertex shader param data. - * fragmentParamOffset: The offset of the fragment shader param data. - */ -REFRESHAPI void Refresh_DrawPrimitivesIndirect( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint32_t offsetInBytes, - uint32_t drawCount, - uint32_t stride, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset -); - -/* Dispatches work compute items. - * - * groupCountX: Number of local workgroups to dispatch in the X dimension. - * groupCountY: Number of local workgroups to dispatch in the Y dimension. - * groupCountZ: Number of local workgroups to dispatch in the Z dimension. - * computeParamOffset: The offset of the compute shader param data. - */ -REFRESHAPI void Refresh_DispatchCompute( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - uint32_t groupCountX, - uint32_t groupCountY, - uint32_t groupCountZ, - uint32_t computeParamOffset -); +REFRESHAPI Refresh_Backend Refresh_GetBackend(Refresh_Device *device); /* State Creation */ -/* Returns an allocated ComputePipeline* object. */ +/** + * Creates a pipeline object to be used in a compute workflow. + * + * \param device a GPU Context + * \param computePipelineCreateInfo a struct describing the state of the requested compute pipeline + * \returns a compute pipeline object on success, or NULL on failure + * + * \sa Refresh_CreateShader + * \sa Refresh_BindComputePipeline + * \sa Refresh_ReleaseComputePipeline + */ REFRESHAPI Refresh_ComputePipeline* Refresh_CreateComputePipeline( Refresh_Device *device, - Refresh_ComputeShaderInfo *computeShaderInfo + Refresh_ComputePipelineCreateInfo *computePipelineCreateInfo ); -/* Returns an allocated GraphicsPipeline* object. */ +/** + * Creates a pipeline object to be used in a graphics workflow. + * + * \param device a GPU Context + * \param pipelineCreateInfo a struct describing the state of the desired graphics pipeline + * \returns a graphics pipeline object on success, or NULL on failure + * + * \sa Refresh_CreateShader + * \sa Refresh_BindGraphicsPipeline + * \sa Refresh_ReleaseGraphicsPipeline + */ REFRESHAPI Refresh_GraphicsPipeline* Refresh_CreateGraphicsPipeline( Refresh_Device *device, Refresh_GraphicsPipelineCreateInfo *pipelineCreateInfo ); -/* Returns an allocated Sampler* object. */ +/** + * Creates a sampler object to be used when binding textures in a graphics workflow. + * + * \param device a GPU Context + * \param samplerCreateInfo a struct describing the state of the desired sampler + * \returns a sampler object on success, or NULL on failure + * + * \sa Refresh_BindVertexSamplers + * \sa Refresh_BindFragmentSamplers + * \sa SDL_ReleaseSampler + */ REFRESHAPI Refresh_Sampler* Refresh_CreateSampler( Refresh_Device *device, - Refresh_SamplerStateCreateInfo *samplerStateCreateInfo + Refresh_SamplerCreateInfo *samplerCreateInfo ); -/* Returns an allocated ShaderModule* object. */ -REFRESHAPI Refresh_ShaderModule* Refresh_CreateShaderModule( +/** + * Creates a shader to be used when creating a graphics or compute pipeline. + * + * \param device a GPU Context + * \param shaderCreateInfo a struct describing the state of the desired shader + * \returns a shader object on success, or NULL on failure + * + * \sa Refresh_CreateGraphicsPipeline + * \sa Refresh_CreateComputePipeline + * \sa Refresh_ReleaseShader + */ +REFRESHAPI Refresh_Shader* Refresh_CreateShader( Refresh_Device *device, - Refresh_ShaderModuleCreateInfo *shaderModuleCreateInfo + Refresh_ShaderCreateInfo *shaderCreateInfo ); -/* Returns an allocated Refresh_Texture* object. Note that the contents of - * the texture are undefined until SetData is called. +/** + * Creates a texture object to be used in graphics or compute workflows. + * The contents of this texture are undefined until data is written to the texture. + * + * Note that certain combinations of usage flags are invalid. + * For example, a texture cannot have both the SAMPLER and GRAPHICS_STORAGE_READ flags. + * + * \param device a GPU Context + * \param textureCreateInfo a struct describing the state of the texture to create + * \returns a texture object on success, or NULL on failure + * + * \sa Refresh_UploadToTexture + * \sa Refresh_DownloadFromTexture + * \sa Refresh_BindVertexSamplers + * \sa Refresh_BindVertexStorageTextures + * \sa Refresh_BindFragmentSamplers + * \sa Refresh_BindFragmentStorageTextures + * \sa Refresh_BindComputeStorageTextures + * \sa Refresh_Blit + * \sa Refresh_ReleaseTexture */ REFRESHAPI Refresh_Texture* Refresh_CreateTexture( Refresh_Device *device, Refresh_TextureCreateInfo *textureCreateInfo ); -/* Creates a buffer. +/** + * Creates a buffer object to be used in graphics or compute workflows. + * The contents of this buffer are undefined until data is written to the buffer. * - * usageFlags: Specifies how the buffer will be used. - * sizeInBytes: The length of the buffer. + * Note that certain combinations of usage flags are invalid. + * For example, a buffer cannot have both the VERTEX and INDEX flags. + * + * \param device a GPU Context + * \param usageFlags bitflag mask hinting at how the buffer will be used + * \param sizeInBytes the size of the buffer + * \returns a buffer object on success, or NULL on failure + * + * \sa Refresh_UploadToBuffer + * \sa Refresh_BindVertexBuffers + * \sa Refresh_BindIndexBuffer + * \sa Refresh_BindVertexStorageBuffers + * \sa Refresh_BindFragmentStorageBuffers + * \sa Refresh_BindComputeStorageBuffers + * \sa Refresh_ReleaseBuffer */ REFRESHAPI Refresh_Buffer* Refresh_CreateBuffer( Refresh_Device *device, Refresh_BufferUsageFlags usageFlags, - uint32_t sizeInBytes + Uint32 sizeInBytes ); -/* Setters */ - -/* Uploads image data to a texture object. +/** + * Creates a transfer buffer to be used when uploading to or downloading from graphics resources. * - * NOTE: - * DO NOT expect this to execute in sequence relative to other commands! - * Calling SetTextureData in a command buffer that also references the - * texture may result in undefined behavior. + * \param device a GPU Context + * \param usage specifies whether the transfer buffer will transfer buffers or textures + * \param mapFlags specify read-write options for the transfer buffer + * \param sizeInBytes the size of the transfer buffer + * \returns a transfer buffer on success, or NULL on failure * - * textureSlice: The texture slice to be updated. - * data: A pointer to the image data. - * dataLengthInBytes: The size of the image data. + * \sa Refresh_UploadToBuffer + * \sa Refresh_DownloadFromBuffer + * \sa Refresh_UploadToTexture + * \sa Refresh_DownloadFromTexture + * \sa Refresh_ReleaseTransferBuffer */ -REFRESHAPI void Refresh_SetTextureData( - Refresh_Device *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *textureSlice, - void *data, - uint32_t dataLengthInBytes -); - -/* Uploads YUV image data to three R8 texture objects. - * - * y: The texture storing the Y data. - * u: The texture storing the U (Cb) data. - * v: The texture storing the V (Cr) data. - * yWidth: The width of the Y plane. - * yHeight: The height of the Y plane. - * uvWidth: The width of the U/V planes. - * uvHeight: The height of the U/V planes. - * yData: A pointer to the raw Y image data. - * uData: A pointer to the raw U image data. - * vData: A pointer to the raw V image data. - * yDataLength: The size of the Y image data in bytes. - * uvDataLength: The size of the UV image data in bytes. - * yStride: The length of a Y image data row in bytes. - * uvStride: The length of a UV image data row in bytes. - */ -REFRESHAPI void Refresh_SetTextureDataYUV( - Refresh_Device *driverData, - Refresh_CommandBuffer* commandBuffer, - Refresh_Texture *y, - Refresh_Texture *u, - Refresh_Texture *v, - uint32_t yWidth, - uint32_t yHeight, - uint32_t uvWidth, - uint32_t uvHeight, - void *yDataPtr, - void *uDataPtr, - void *vDataPtr, - uint32_t yDataLength, - uint32_t uvDataLength, - uint32_t yStride, - uint32_t uvStride -); - -/* Performs an asynchronous texture-to-texture copy. - * - * sourceTextureSlice: The texture slice from which to copy. - * destinationTextureSlice: The texture slice to copy to. - * filter: The filter that will be used if the copy requires scaling. - */ -REFRESHAPI void Refresh_CopyTextureToTexture( - Refresh_Device *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *sourceTextureSlice, - Refresh_TextureSlice *destinationTextureSlice, - Refresh_Filter filter -); - -/* Asynchronously copies image data from a texture slice into a buffer. - * - * NOTE: - * The buffer will not contain correct data until the command buffer - * is submitted and completed. - * - * textureSlice: The texture object being copied. - * buffer: The buffer being filled with the image data. - */ -REFRESHAPI void Refresh_CopyTextureToBuffer( +REFRESHAPI Refresh_TransferBuffer* Refresh_CreateTransferBuffer( Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *textureSlice, - Refresh_Buffer *buffer + Refresh_TransferUsage usage, + Refresh_TransferBufferMapFlags mapFlags, + Uint32 sizeInBytes ); -/* Sets a region of the buffer with client data. +/** + * Creates an occlusion query object. * - * NOTE: - * Calling this function on a buffer after the buffer - * has been bound without calling Submit first is an error. + * This function is included for compatibility with old render systems. + * If you are not supporting an old application, do NOT use this. * - * buffer: The vertex buffer to be updated. - * offsetInBytes: The starting offset of the buffer to write into. - * data: The client data to write into the buffer. - * dataLength: The length of data from the client buffer to write. + * \param device a GPU context + * \returns an occlusion query object + * + * \sa Refresh_ReleaseQuery */ -REFRESHAPI void Refresh_SetBufferData( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint32_t offsetInBytes, - void* data, - uint32_t dataLength +REFRESHAPI Refresh_OcclusionQuery* Refresh_CreateOcclusionQuery( + Refresh_Device *device ); -/* Pushes vertex shader params to the device. - * Returns a starting offset value to be used with draw calls. - * - * NOTE: - * A pipeline must be bound. - * Will use the block size of the currently bound vertex shader. - * - * data: The client data to write into the buffer. - * dataLengthInBytes: The length of the data to write. - */ -REFRESHAPI uint32_t Refresh_PushVertexShaderUniforms( - Refresh_Device *device, - Refresh_CommandBuffer * commandBuffer, - void *data, - uint32_t dataLengthInBytes -); +/* Debug Naming */ -/* Pushes fragment shader params to the device. - * Returns a starting offset value to be used with draw calls. +/** + * Sets an arbitrary string constant to label a buffer. Useful for debugging. * - * NOTE: - * A graphics pipeline must be bound. - * Will use the block size of the currently bound fragment shader. - * - * data: The client data to write into the buffer. - * dataLengthInBytes: The length of the data to write. + * \param device a GPU Context + * \param buffer a buffer to attach the name to + * \param text a UTF-8 string constant to mark as the name of the buffer */ -REFRESHAPI uint32_t Refresh_PushFragmentShaderUniforms( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - void *data, - uint32_t dataLengthInBytes -); - -/* Pushes compute shader params to the device. - * Returns a starting offset value to be used with draw calls. - * - * NOTE: - * A compute pipeline must be bound. - * Will use the block size of the currently bound compute shader. - * - * data: The client data to write into the buffer. - * dataLengthInBytes: The length of the data to write. - */ -REFRESHAPI uint32_t Refresh_PushComputeShaderUniforms( - Refresh_Device *device, - Refresh_CommandBuffer * commandBuffer, - void *data, - uint32_t dataLengthInBytes -); - -/* Getters */ - -/* Synchronously copies data from a buffer to a pointer. - * You probably want to wait for a sync point to call this. - * - * buffer: The buffer to copy data from. - * data: The pointer to copy data to. - * dataLengthInBytes: The length of data to copy. - */ -REFRESHAPI void Refresh_GetBufferData( +REFRESHAPI void Refresh_SetBufferName( Refresh_Device *device, Refresh_Buffer *buffer, - void *data, - uint32_t dataLengthInBytes + const char *text +); + +/** + * Sets an arbitrary string constant to label a texture. Useful for debugging. + * + * \param device a GPU Context + * \param texture a texture to attach the name to + * \param text a UTF-8 string constant to mark as the name of the texture + */ +REFRESHAPI void Refresh_SetTextureName( + Refresh_Device *device, + Refresh_Texture *texture, + const char *text +); + +/** + * Sets an arbitrary string constant to label a section of a command buffer. Useful for debugging. + * + * \param commandBuffer a command buffer + * \param text a UTF-8 string constant to mark as the label + */ +REFRESHAPI void Refresh_SetStringMarker( + Refresh_CommandBuffer *commandBuffer, + const char *text ); /* Disposal */ -/* Sends a texture to be destroyed by the renderer. Note that we call it - * "QueueDestroy" because it may not be immediately destroyed by the renderer if - * this is not called from the main thread (for example, if a garbage collector - * deletes the resource instead of the programmer). +/** + * Frees the given texture as soon as it is safe to do so. + * You must not reference the texture after calling this function. * - * texture: The Refresh_Texture to be destroyed. + * \param device a GPU context + * \param texture a texture to be destroyed */ -REFRESHAPI void Refresh_QueueDestroyTexture( +REFRESHAPI void Refresh_ReleaseTexture( Refresh_Device *device, Refresh_Texture *texture ); -/* Sends a sampler to be destroyed by the renderer. Note that we call it - * "QueueDestroy" because it may not be immediately destroyed by the renderer if - * this is not called from the main thread (for example, if a garbage collector - * deletes the resource instead of the programmer). +/** + * Frees the given sampler as soon as it is safe to do so. + * You must not reference the texture after calling this function. * - * texture: The Refresh_Sampler to be destroyed. + * \param device a GPU context + * \param sampler a sampler to be destroyed */ -REFRESHAPI void Refresh_QueueDestroySampler( +REFRESHAPI void Refresh_ReleaseSampler( Refresh_Device *device, Refresh_Sampler *sampler ); -/* Sends a buffer to be destroyed by the renderer. Note that we call it - * "QueueDestroy" because it may not be immediately destroyed by the renderer if - * this is not called from the main thread (for example, if a garbage collector - * deletes the resource instead of the programmer). +/** + * Frees the given buffer as soon as it is safe to do so. + * You must not reference the buffer after calling this function. * - * buffer: The Refresh_Buffer to be destroyed. + * \param device a GPU context + * \param buffer a buffer to be destroyed */ -REFRESHAPI void Refresh_QueueDestroyBuffer( +REFRESHAPI void Refresh_ReleaseBuffer( Refresh_Device *device, Refresh_Buffer *buffer ); -/* Sends a shader module to be destroyed by the renderer. Note that we call it - * "QueueDestroy" because it may not be immediately destroyed by the renderer if - * this is not called from the main thread (for example, if a garbage collector - * deletes the resource instead of the programmer). +/** + * Frees the given transfer buffer as soon as it is safe to do so. + * You must not reference the transfer buffer after calling this function. * - * shaderModule: The Refresh_ShaderModule to be destroyed. + * \param device a GPU context + * \param transferBuffer a transfer buffer to be destroyed */ -REFRESHAPI void Refresh_QueueDestroyShaderModule( +REFRESHAPI void Refresh_ReleaseTransferBuffer( Refresh_Device *device, - Refresh_ShaderModule *shaderModule + Refresh_TransferBuffer *transferBuffer ); -/* Sends a compute pipeline to be destroyed by the renderer. Note that we call it - * "QueueDestroy" because it may not be immediately destroyed by the renderer if - * this is not called from the main thread (for example, if a garbage collector - * deletes the resource instead of the programmer). +/** + * Frees the given shader as soon as it is safe to do so. + * You must not reference the shader after calling this function. * - * computePipeline: The Refresh_ComputePipeline to be destroyed. + * \param device a GPU context + * \param shader a shader to be destroyed */ -REFRESHAPI void Refresh_QueueDestroyComputePipeline( +REFRESHAPI void Refresh_ReleaseShader( + Refresh_Device *device, + Refresh_Shader *shader +); + +/** + * Frees the given compute pipeline as soon as it is safe to do so. + * You must not reference the compute pipeline after calling this function. + * + * \param device a GPU context + * \param computePipeline a compute pipeline to be destroyed + */ +REFRESHAPI void Refresh_ReleaseComputePipeline( Refresh_Device *device, Refresh_ComputePipeline *computePipeline ); -/* Sends a graphics pipeline to be destroyed by the renderer. Note that we call it - * "QueueDestroy" because it may not be immediately destroyed by the renderer if - * this is not called from the main thread (for example, if a garbage collector - * deletes the resource instead of the programmer). +/** + * Frees the given graphics pipeline as soon as it is safe to do so. + * You must not reference the graphics pipeline after calling this function. * - * graphicsPipeline: The Refresh_GraphicsPipeline to be destroyed. + * \param device a GPU context + * \param graphicsPipeline a graphics pipeline to be destroyed */ -REFRESHAPI void Refresh_QueueDestroyGraphicsPipeline( +REFRESHAPI void Refresh_ReleaseGraphicsPipeline( Refresh_Device *device, Refresh_GraphicsPipeline *graphicsPipeline ); +/** + * Frees the given occlusion query as soon as it is safe to do so. + * You must not reference the occlusion query after calling this function. + * + * \param device a GPU context + * \param query an occlusion query object to be destroyed + */ +REFRESHAPI void Refresh_ReleaseOcclusionQuery( + Refresh_Device *device, + Refresh_OcclusionQuery *query +); + +/* + * A NOTE ON CYCLING + * + * When using a command buffer, operations do not occur immediately - + * they occur some time after the command buffer is submitted. + * + * When a resource is used in a pending or active command buffer, it is considered to be "bound". + * When a resource is no longer used in any pending or active command buffers, it is considered to be "unbound". + * + * If data resources are bound, it is unspecified when that data will be unbound + * unless you acquire a fence when submitting the command buffer and wait on it. + * However, this doesn't mean you need to track resource usage manually. + * + * All of the functions and structs that involve writing to a resource have a "cycle" bool. + * GpuTransferBuffer, GpuBuffer, and GpuTexture all effectively function as ring buffers on internal resources. + * When cycle is SDL_TRUE, if the resource is bound, the cycle rotates to the next unbound internal resource, + * or if none are available, a new one is created. + * This means you don't have to worry about complex state tracking and synchronization as long as cycling is correctly employed. + * + * For example: you can call SetTransferData and then UploadToTexture. The next time you call SetTransferData, + * if you set the cycle param to SDL_TRUE, you don't have to worry about overwriting any data that is not yet uploaded. + * + * Another example: If you are using a texture in a render pass every frame, this can cause a data dependency between frames. + * If you set cycle to SDL_TRUE in the ColorAttachmentInfo struct, you can prevent this data dependency. + * + * Note that all functions which write to a texture specifically write to a GpuTextureSlice, + * and these slices themselves are tracked for binding. + * The GpuTexture will only cycle if the specific GpuTextureSlice being written to is bound. + * + * Cycling will never undefine already bound data. + * When cycling, all data in the resource is considered to be undefined for subsequent commands until that data is written again. + * You must take care not to read undefined data. + * + * You must also take care not to overwrite a section of data that has been referenced in a command without cycling first. + * It is OK to overwrite unreferenced data in a bound resource without cycling, + * but overwriting a section of data that has already been referenced will produce unexpected results. + */ + /* Graphics State */ -/* Begins a render pass. - * This will also set a default viewport and scissor state. +/** + * Begins a render pass on a command buffer. + * A render pass consists of a set of texture slices, clear values, and load/store operations + * which will be rendered to during the render pass. + * All operations related to graphics pipelines must take place inside of a render pass. + * A default viewport and scissor state are automatically set when this is called. + * You cannot begin another render pass, or begin a compute pass or copy pass + * until you have ended the render pass. * - * colorAttachmentInfos: - * A pointer to an array of Refresh_ColorAttachmentInfo structures - * that contains render targets and clear values. May be NULL. - * colorAttachmentCount: The amount of structs in the above array. - * depthStencilAttachmentInfo: The depth/stencil render target and clear value. May be NULL. + * \param commandBuffer a command buffer + * \param colorAttachmentInfos an array of Refresh_ColorAttachmentInfo structs + * \param colorAttachmentCount the number of color attachments in the colorAttachmentInfos array + * \param depthStencilAttachmentInfo the depth-stencil target and clear value, may be NULL + * \returns a render pass handle + * + * \sa Refresh_EndRenderPass */ -REFRESHAPI void Refresh_BeginRenderPass( - Refresh_Device *device, +REFRESHAPI Refresh_RenderPass* Refresh_BeginRenderPass( Refresh_CommandBuffer *commandBuffer, Refresh_ColorAttachmentInfo *colorAttachmentInfos, - uint32_t colorAttachmentCount, + Uint32 colorAttachmentCount, Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo ); -/* Ends the current render pass. */ -REFRESHAPI void Refresh_EndRenderPass( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer -); - -/* Binds a graphics pipeline to the graphics bind point. */ +/** + * Binds a graphics pipeline on a render pass to be used in rendering. + * A graphics pipeline must be bound before making any draw calls. + * + * \param renderPass a render pass handle + * \param graphicsPipeline the graphics pipeline to bind + */ REFRESHAPI void Refresh_BindGraphicsPipeline( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, + Refresh_RenderPass *renderPass, Refresh_GraphicsPipeline *graphicsPipeline ); -/* Sets the current viewport state. */ +/** + * Sets the current viewport state on a command buffer. + * + * \param renderPass a render pass handle + * \param viewport the viewport to set + */ REFRESHAPI void Refresh_SetViewport( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, + Refresh_RenderPass *renderPass, Refresh_Viewport *viewport ); -/* Sets the current scissor state. */ +/** + * Sets the current scissor state on a command buffer. + * + * \param renderPass a render pass handle + * \param scissor the scissor area to set + */ REFRESHAPI void Refresh_SetScissor( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, + Refresh_RenderPass *renderPass, Refresh_Rect *scissor ); -/* Binds vertex buffers for use with subsequent draw calls. - * Note that this may only be called after binding a graphics pipeline. +/** + * Binds vertex buffers on a command buffer for use with subsequent draw calls. + * + * \param renderPass a render pass handle + * \param firstBinding the starting bind point for the vertex buffers + * \param pBindings an array of Refresh_BufferBinding structs containing vertex buffers and offset values + * \param bindingCount the number of bindings in the pBindings array */ REFRESHAPI void Refresh_BindVertexBuffers( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - uint32_t firstBinding, - uint32_t bindingCount, - Refresh_Buffer **pBuffers, - uint64_t *pOffsets + Refresh_RenderPass *renderPass, + Uint32 firstBinding, + Refresh_BufferBinding *pBindings, + Uint32 bindingCount ); -/* Binds an index buffer for use with subsequent draw calls. */ +/** + * Binds an index buffer on a command buffer for use with subsequent draw calls. + * + * \param renderPass a render pass handle + * \param pBinding a pointer to a struct containing an index buffer and offset + * \param indexElementSize whether the index values in the buffer are 16- or 32-bit + */ REFRESHAPI void Refresh_BindIndexBuffer( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint64_t offset, + Refresh_RenderPass *renderPass, + Refresh_BufferBinding *pBinding, Refresh_IndexElementSize indexElementSize ); -/* Sets textures/samplers for use with the currently bound vertex shader. +/** + * Binds texture-sampler pairs for use on the vertex shader. + * The textures must have been created with REFRESH_TEXTUREUSAGE_SAMPLER_BIT. * - * NOTE: - * The length of the passed arrays must be equal to the number - * of sampler bindings specified by the pipeline. - * - * textures: A pointer to an array of textures. - * samplers: A pointer to an array of samplers. + * \param renderPass a render pass handle + * \param firstSlot the vertex sampler slot to begin binding from + * \param textureSamplerBindings an array of texture-sampler binding structs + * \param bindingCount the number of texture-sampler pairs to bind from the array */ REFRESHAPI void Refresh_BindVertexSamplers( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures, - Refresh_Sampler **pSamplers + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_TextureSamplerBinding *textureSamplerBindings, + Uint32 bindingCount ); -/* Sets textures/samplers for use with the currently bound fragment shader. +/** + * Binds storage textures for use on the vertex shader. + * These textures must have been created with REFRESH_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT. * - * NOTE: - * The length of the passed arrays must be equal to the number - * of sampler bindings specified by the pipeline. + * \param renderPass a render pass handle + * \param firstSlot the vertex storage texture slot to begin binding from + * \param storageTextureSlices an array of storage texture slices + * \param bindingCount the number of storage texture slices to bind from the array + */ +REFRESHAPI void Refresh_BindVertexStorageTextures( + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount +); + +/** + * Binds storage buffers for use on the vertex shader. + * These buffers must have been created with REFRESH_BUFFERUSAGE_GRAPHICS_STORAGE_READ_BIT. * - * textures: A pointer to an array of textures. - * samplers: A pointer to an array of samplers. + * \param renderPass a render pass handle + * \param firstSlot the vertex storage buffer slot to begin binding from + * \param storageBuffers an array of buffers + * \param bindingCount the number of buffers to bind from the array + */ +REFRESHAPI void Refresh_BindVertexStorageBuffers( + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +); + +/** + * Binds texture-sampler pairs for use on the fragment shader. + * The textures must have been created with REFRESH_TEXTUREUSAGE_SAMPLER_BIT. + * + * \param renderPass a render pass handle + * \param firstSlot the fragment sampler slot to begin binding from + * \param textureSamplerBindings an array of texture-sampler binding structs + * \param bindingCount the number of texture-sampler pairs to bind from the array */ REFRESHAPI void Refresh_BindFragmentSamplers( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures, - Refresh_Sampler **pSamplers + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_TextureSamplerBinding *textureSamplerBindings, + Uint32 bindingCount ); -/* Binds a compute pipeline to the compute bind point. */ -REFRESHAPI void Refresh_BindComputePipeline( - Refresh_Device *device, +/** + * Binds storage textures for use on the fragment shader. + * These textures must have been created with REFRESH_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT. + * + * \param renderPass a render pass handle + * \param firstSlot the fragment storage texture slot to begin binding from + * \param storageTextureSlices an array of storage texture slices + * \param bindingCount the number of storage texture slices to bind from the array + */ +REFRESHAPI void Refresh_BindFragmentStorageTextures( + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount +); + +/** + * Binds storage buffers for use on the fragment shader. + * These buffers must have been created with REFRESH_BUFFERUSAGE_GRAPHICS_STORAGE_READ_BIT. + * + * \param renderPass a render pass handle + * \param firstSlot the fragment storage buffer slot to begin binding from + * \param storageBuffers an array of storage buffers + * \param bindingCount the number of storage buffers to bind from the array + */ +REFRESHAPI void Refresh_BindFragmentStorageBuffers( + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +); + +/** + * Pushes data to a vertex uniform slot on the bound graphics pipeline. + * Subsequent draw calls will use this uniform data. + * + * \param renderPass a render pass handle + * \param slotIndex the vertex uniform slot to push data to + * \param data client data to write + * \param dataLengthInBytes the length of the data to write + */ +REFRESHAPI void Refresh_PushVertexUniformData( + Refresh_RenderPass *renderPass, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +); + +/** + * Pushes data to a fragment uniform slot on the bound graphics pipeline. + * Subsequent draw calls will use this uniform data. + * + * \param renderPass a render pass handle + * \param slotIndex the fragment uniform slot to push data to + * \param data client data to write + * \param dataLengthInBytes the length of the data to write + */ +REFRESHAPI void Refresh_PushFragmentUniformData( + Refresh_RenderPass *renderPass, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +); + +/* Drawing */ + +/** + * Draws data using bound graphics state with an index buffer and instancing enabled. + * You must not call this function before binding a graphics pipeline. + * + * \param renderPass a render pass handle + * \param baseVertex the starting offset to read from the vertex buffer + * \param startIndex the starting offset to read from the index buffer + * \param primitiveCount the number of primitives to draw + * \param instanceCount the number of instances that will be drawn + */ +REFRESHAPI void Refresh_DrawIndexedPrimitives( + Refresh_RenderPass *renderPass, + Uint32 baseVertex, + Uint32 startIndex, + Uint32 primitiveCount, + Uint32 instanceCount +); + +/** + * Draws data using bound graphics state. + * You must not call this function before binding a graphics pipeline. + * + * \param renderPass a render pass handle + * \param vertexStart The starting offset to read from the vertex buffer + * \param primitiveCount The number of primitives to draw + */ +REFRESHAPI void Refresh_DrawPrimitives( + Refresh_RenderPass *renderPass, + Uint32 vertexStart, + Uint32 primitiveCount +); + +/** + * Draws data using bound graphics state and with draw parameters set from a buffer. + * The buffer layout should match the layout of Refresh_IndirectDrawCommand. + * You must not call this function before binding a graphics pipeline. + * + * \param renderPass a render pass handle + * \param buffer a buffer containing draw parameters + * \param offsetInBytes the offset to start reading from the draw buffer + * \param drawCount the number of draw parameter sets that should be read from the draw buffer + * \param stride the byte stride between sets of draw parameters + */ +REFRESHAPI void Refresh_DrawPrimitivesIndirect( + Refresh_RenderPass *renderPass, + Refresh_Buffer *buffer, + Uint32 offsetInBytes, + Uint32 drawCount, + Uint32 stride +); + +/** + * Draws data using bound graphics state with an index buffer enabled + * and with draw parameters set from a buffer. + * The buffer layout should match the layout of Refresh_IndexedIndirectDrawCommand. + * You must not call this function before binding a graphics pipeline. + * + * \param renderPass a render pass handle + * \param buffer a buffer containing draw parameters + * \param offsetInBytes the offset to start reading from the draw buffer + * \param drawCount the number of draw parameter sets that should be read from the draw buffer + * \param stride the byte stride between sets of draw parameters + */ +REFRESHAPI void Refresh_DrawIndexedPrimitivesIndirect( + Refresh_RenderPass *renderPass, + Refresh_Buffer *buffer, + Uint32 offsetInBytes, + Uint32 drawCount, + Uint32 stride +); + +/** + * Ends the given render pass. + * All bound graphics state on the render pass command buffer is unset. + * The render pass handle is now invalid. + * + * \param renderPass a render pass handle + */ +REFRESHAPI void Refresh_EndRenderPass( + Refresh_RenderPass *renderPass +); + +/* Compute Pass */ + +/** + * Begins a compute pass on a command buffer. + * A compute pass is defined by a set of texture slices and buffers that + * will be written to by compute pipelines. + * These textures and buffers must have been created with the COMPUTE_STORAGE_WRITE bit. + * If these resources will also be read during the pass, they must be created with the COMPUTE_STORAGE_READ bit. + * All operations related to compute pipelines must take place inside of a compute pass. + * You must not begin another compute pass, or a render pass or copy pass + * before ending the compute pass. + * + * \param commandBuffer a command buffer + * \param storageTextureBindings an array of writeable storage texture binding structs + * \param storageTextureBindingCount the number of storage textures to bind from the array + * \param storageBufferBindings an array of writeable storage buffer binding structs + * \param storageBufferBindingCount an array of read-write storage buffer binding structs + * + * \returns a compute pass handle + * + * \sa Refresh_EndComputePass + */ +REFRESHAPI Refresh_ComputePass* Refresh_BeginComputePass( Refresh_CommandBuffer *commandBuffer, + Refresh_StorageTextureReadWriteBinding *storageTextureBindings, + Uint32 storageTextureBindingCount, + Refresh_StorageBufferReadWriteBinding *storageBufferBindings, + Uint32 storageBufferBindingCount +); + +/** + * Binds a compute pipeline on a command buffer for use in compute dispatch. + * + * \param computePass a compute pass handle + * \param computePipeline a compute pipeline to bind + */ +REFRESHAPI void Refresh_BindComputePipeline( + Refresh_ComputePass *computePass, Refresh_ComputePipeline *computePipeline ); -/* Binds buffers for use with the currently bound compute pipeline. +/** + * Binds storage textures as readonly for use on the compute shader. + * These textures must have been created with REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT. * - * pBuffers: An array of buffers to bind. - * Length must be equal to the number of buffers - * specified by the compute pipeline. + * \param computePass a compute pass handle + * \param firstSlot the compute storage texture slot to begin binding from + * \param storageTextureSlices an array of storage texture binding structs + * \param bindingCount the number of storage textures to bind from the array */ -REFRESHAPI void Refresh_BindComputeBuffers( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer **pBuffers +REFRESHAPI void Refresh_BindComputeStorageTextures( + Refresh_ComputePass *computePass, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount ); -/* Binds textures for use with the currently bound compute pipeline. +/** + * Binds storage buffers as readonly for use on the compute shader. + * These buffers must have been created with REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_READ_BIT. * - * pTextures: An array of textures to bind. - * Length must be equal to the number of buffers - * specified by the compute pipeline. + * \param computePass a compute pass handle + * \param firstSlot the compute storage buffer slot to begin binding from + * \param storageBuffers an array of storage buffer binding structs + * \param bindingCount the number of storage buffers to bind from the array */ -REFRESHAPI void Refresh_BindComputeTextures( +REFRESHAPI void Refresh_BindComputeStorageBuffers( + Refresh_ComputePass *computePass, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +); + +/** + * Pushes data to a uniform slot on the bound compute pipeline. + * Subsequent draw calls will use this uniform data. + * + * \param computePass a compute pass handle + * \param slotIndex the uniform slot to push data to + * \param data client data to write + * \param dataLengthInBytes the length of the data to write + */ +REFRESHAPI void Refresh_PushComputeUniformData( + Refresh_ComputePass *computePass, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +); + +/** + * Dispatches compute work. + * You must not call this function before binding a compute pipeline. + * + * A VERY IMPORTANT NOTE + * If you dispatch multiple times in a compute pass, + * and the dispatches write to the same resource region as each other, + * there is no guarantee of which order the writes will occur. + * If the write order matters, you MUST end the compute pass and begin another one. + * + * \param computePass a compute pass handle + * \param groupCountX number of local workgroups to dispatch in the X dimension + * \param groupCountY number of local workgroups to dispatch in the Y dimension + * \param groupCountZ number of local workgroups to dispatch in the Z dimension + */ +REFRESHAPI void Refresh_DispatchCompute( + Refresh_ComputePass *computePass, + Uint32 groupCountX, + Uint32 groupCountY, + Uint32 groupCountZ +); + +/** + * Ends the current compute pass. + * All bound compute state on the command buffer is unset. + * The compute pass handle is now invalid. + * + * \param computePass a compute pass handle + */ +REFRESHAPI void Refresh_EndComputePass( + Refresh_ComputePass *computePass +); + +/* TransferBuffer Data */ + +/** + * Maps a transfer buffer into application address space. + * You must unmap the transfer buffer before encoding upload commands. + * + * \param device a GPU context + * \param transferBuffer a transfer buffer + * \param cycle if SDL_TRUE, cycles the transfer buffer if it is bound + * \param ppData where to store the address of the mapped transfer buffer memory + */ +REFRESHAPI void Refresh_MapTransferBuffer( + Refresh_Device *device, + Refresh_TransferBuffer *transferBuffer, + SDL_bool cycle, + void **ppData +); + +/** + * Unmaps a previously mapped transfer buffer. + * + * \param device a GPU context + * \param transferBuffer a previously mapped transfer buffer + */ +REFRESHAPI void Refresh_UnmapTransferBuffer( + Refresh_Device *device, + Refresh_TransferBuffer *transferBuffer +); + +/** + * Immediately copies data from a pointer to a transfer buffer. + * + * \param device a GPU context + * \param data a pointer to data to copy into the transfer buffer + * \param transferBuffer a transfer buffer + * \param copyParams a struct containing parameters specifying copy offsets and size + * \param cycle if SDL_TRUE, cycles the transfer buffer if it is bound, otherwise overwrites the data. + */ +REFRESHAPI void Refresh_SetTransferData( Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures + void* data, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +); + +/** + * Immediately copies data from a transfer buffer to a pointer. + * + * \param device a GPU context + * \param transferBuffer a transfer buffer + * \param data a data pointer + * \param copyParams a struct containing parameters specifying copy offsets and size + */ +REFRESHAPI void Refresh_GetTransferData( + Refresh_Device *device, + Refresh_TransferBuffer *transferBuffer, + void* data, + Refresh_BufferCopy *copyParams +); + +/* Copy Pass */ + +/** + * Begins a copy pass on a command buffer. + * All operations related to copying to or from buffers or textures take place inside a copy pass. + * You must not begin another copy pass, or a render pass or compute pass + * before ending the copy pass. + * + * \param commandBuffer a command buffer + * \returns a copy pass handle + */ +REFRESHAPI Refresh_CopyPass* Refresh_BeginCopyPass( + Refresh_CommandBuffer *commandBuffer +); + +/** + * Uploads data from a transfer buffer to a texture. + * The upload occurs on the GPU timeline. + * You may assume that the upload has finished in subsequent commands. + * + * You must align the data in the transfer buffer to a multiple of + * the texel size of the texture format. + * + * \param copyPass a copy pass handle + * \param transferBuffer a transfer buffer + * \param textureRegion a struct containing parameters specifying the texture region to upload data to + * \param copyParams a struct containing parameters specifying buffer offset, stride, and height + * \param cycle if SDL_TRUE, cycles the texture if the texture slice is bound, otherwise overwrites the data. + */ +REFRESHAPI void Refresh_UploadToTexture( + Refresh_CopyPass *copyPass, + Refresh_TransferBuffer *transferBuffer, + Refresh_TextureRegion *textureRegion, + Refresh_BufferImageCopy *copyParams, + SDL_bool cycle +); + +/* Uploads data from a TransferBuffer to a Buffer. */ + +/** + * Uploads data from a transfer buffer to a buffer. + * The upload occurs on the GPU timeline. + * You may assume that the upload has finished in subsequent commands. + * + * \param copyPass a copy pass handle + * \param transferBuffer a transfer buffer + * \param buffer a buffer + * \param copyParams a struct containing offsets and length + * \param cycle if SDL_TRUE, cycles the buffer if it is bound, otherwise overwrites the data. + */ +REFRESHAPI void Refresh_UploadToBuffer( + Refresh_CopyPass *copyPass, + Refresh_TransferBuffer *transferBuffer, + Refresh_Buffer *buffer, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +); + +/** + * Performs a texture-to-texture copy. + * This copy occurs on the GPU timeline. + * You may assume the copy has finished in subsequent commands. + * + * \param copyPass a copy pass handle + * \param source a source texture region + * \param destination must be the same dimensions as the source region + * \param cycle if SDL_TRUE, cycles the destination texture if the destination texture slice is bound, otherwise overwrites the data. + */ +REFRESHAPI void Refresh_CopyTextureToTexture( + Refresh_CopyPass *copyPass, + Refresh_TextureRegion *source, + Refresh_TextureRegion *destination, + SDL_bool cycle +); + +/* Copies data from a buffer to a buffer. */ + +/** + * Performs a buffer-to-buffer copy. + * This copy occurs on the GPU timeline. + * You may assume the copy has finished in subsequent commands. + * + * \param copyPass a copy pass handle + * \param source the buffer to copy from + * \param destination the buffer to copy to + * \param copyParams a struct containing offset and length data + * \param cycle if SDL_TRUE, cycles the destination buffer if it is bound, otherwise overwrites the data. + */ +REFRESHAPI void Refresh_CopyBufferToBuffer( + Refresh_CopyPass *copyPass, + Refresh_Buffer *source, + Refresh_Buffer *destination, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +); + +/** + * Generates mipmaps for the given texture. + * + * \param copyPass a copy pass handle + * \param texture a texture with more than 1 mip level + */ +REFRESHAPI void Refresh_GenerateMipmaps( + Refresh_CopyPass *copyPass, + Refresh_Texture *texture +); + +/** + * Copies data from a texture to a transfer buffer on the GPU timeline. + * This data is not guaranteed to be copied until the command buffer fence is signaled. + * + * \param copyPass a copy pass handle + * \param textureRegion the texture region to download + * \param transferBuffer the transfer buffer to download into + * \param copyParams a struct containing parameters specifying buffer offset, stride, and height + */ +REFRESHAPI void Refresh_DownloadFromTexture( + Refresh_CopyPass *copyPass, + Refresh_TextureRegion *textureRegion, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferImageCopy *copyParams +); + +/** + * Copies data from a buffer to a transfer buffer on the GPU timeline. + * This data is not guaranteed to be copied until the command buffer fence is signaled. + * + * \param copyPass a copy pass handle + * \param buffer the buffer to download + * \param transferBuffer the transfer buffer to download into + * \param copyParams a struct containing offsets and length + */ +REFRESHAPI void Refresh_DownloadFromBuffer( + Refresh_CopyPass *copyPass, + Refresh_Buffer *buffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams +); + +/** + * Ends the current copy pass. + * + * \param copyPass a copy pass handle + */ +REFRESHAPI void Refresh_EndCopyPass( + Refresh_CopyPass *copyPass +); + +/** + * Blits from a source texture region to a destination texture region. + * This function must not be called inside of any render, compute, or copy pass. + * + * \param commandBuffer a command buffer + * \param source the texture region to copy from + * \param destination the texture region to copy to + * \param filterMode the filter mode that will be used when blitting + * \param cycle if SDL_TRUE, cycles the destination texture if the destination texture slice is bound, otherwise overwrites the data. + */ +REFRESHAPI void Refresh_Blit( + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *source, + Refresh_TextureRegion *destination, + Refresh_Filter filterMode, + SDL_bool cycle ); /* Submission/Presentation */ -/* Claims a window, creating a swapchain structure for it. - * This function MUST be called before any swapchain functions - * are called using the window. +/** + * Obtains whether or not a swapchain composition is supported by the GPU backend. * - * Returns 0 on swapchain creation failure. + * \param device a GPU context + * \param window an SDL_Window + * \param swapchainComposition the swapchain composition to check + * + * \returns SDL_TRUE if supported, SDL_FALSE if unsupported (or on error) */ -REFRESHAPI uint8_t Refresh_ClaimWindow( +REFRESHAPI SDL_bool Refresh_SupportsSwapchainComposition( + Refresh_Device *device, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition +); + +/** + * Obtains whether or not a presentation mode is supported by the GPU backend. + * + * \param device a GPU context + * \param window an SDL_Window + * \param presentMode the presentation mode to check + * + * \returns SDL_TRUE if supported, SDL_FALSE if unsupported (or on error) + */ +REFRESHAPI SDL_bool Refresh_SupportsPresentMode( Refresh_Device *device, - void *windowHandle, + SDL_Window *window, Refresh_PresentMode presentMode ); -/* Unclaims a window, destroying the swapchain structure for it. - * It is good practice to call this when a window is closed to - * prevent memory bloat, but windows are automatically unclaimed - * by DestroyDevice. +/** + * Claims a window, creating a swapchain structure for it. + * This must be called before Refresh_AcquireSwapchainTexture is called using the window. + * + * \param device a GPU context + * \param window an SDL_Window + * \param swapchainComposition the desired composition of the swapchain + * \param presentMode the desired present mode for the swapchain + * + * \returns SDL_TRUE on success, otherwise SDL_FALSE. + * + * \sa Refresh_AcquireSwapchainTexture + * \sa Refresh_UnclaimWindow + */ +REFRESHAPI SDL_bool Refresh_ClaimWindow( + Refresh_Device *device, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition, + Refresh_PresentMode presentMode +); + +/** + * Unclaims a window, destroying its swapchain structure. + * + * \param device a GPU context + * \param window an SDL_Window that has been claimed + * + * \sa Refresh_ClaimWindow */ REFRESHAPI void Refresh_UnclaimWindow( Refresh_Device *device, - void *windowHandle + SDL_Window *window ); -/* Changes the present mode of the swapchain for the given window. */ -REFRESHAPI void Refresh_SetSwapchainPresentMode( - Refresh_Device *device, - void *windowHandle, - Refresh_PresentMode presentMode -); - -/* Returns the format of the swapchain for the given window. */ -REFRESHAPI Refresh_TextureFormat Refresh_GetSwapchainFormat( - Refresh_Device *device, - void *windowHandle -); - -/* Returns an allocated Refresh_CommandBuffer* object. - * This command buffer is managed by the implementation and - * should NOT be freed by the user. +/** + * Changes the swapchain parameters for the given claimed window. * - * NOTE: - * A command buffer may only be used on the thread that - * it was acquired on. Using it on any other thread is an error. + * \param device a GPU context + * \param window an SDL_Window that has been claimed + * \param swapchainComposition the desired composition of the swapchain + * \param presentMode the desired present mode for the swapchain + */ +REFRESHAPI void Refresh_SetSwapchainParameters( + Refresh_Device *device, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition, + Refresh_PresentMode presentMode +); + +/** + * Obtains the texture format of the swapchain for the given window. * + * \param device a GPU context + * \param window an SDL_Window that has been claimed + * + * \returns the texture format of the swapchain + */ +REFRESHAPI Refresh_TextureFormat Refresh_GetSwapchainTextureFormat( + Refresh_Device *device, + SDL_Window *window +); + +/** + * Acquire a command buffer. + * This command buffer is managed by the implementation and should not be freed by the user. + * A command buffer may only be used on the thread it was acquired on. + * + * \param device a GPU context + * \returns a command buffer + * + * \sa Refresh_Submit + * \sa Refresh_SubmitAndAcquireFence */ REFRESHAPI Refresh_CommandBuffer* Refresh_AcquireCommandBuffer( Refresh_Device *device ); -/* Acquires a texture to use for presentation. - * May return NULL under certain conditions. - * If NULL, the user must ensure to not use the texture. - * Once a swapchain texture is acquired, - * it will automatically be presented on command buffer submission. +/** + * Acquire a texture to use in presentation. + * When a swapchain texture is acquired on a command buffer, + * it will automatically be submitted for presentation when the command buffer is submitted. + * May return NULL under certain conditions. This is not necessarily an error. + * This texture is managed by the implementation and must not be freed by the user. + * You MUST NOT call this function from any thread other than the one that created the window. * - * NOTE: - * It is not recommended to hold a reference to this texture long term. + * \param commandBuffer a command buffer + * \param window a window that has been claimed + * \param pWidth a pointer filled in with the swapchain width + * \param pHeight a pointer filled in with the swapchain height + * \returns a swapchain texture * - * pWidth: A pointer to a uint32 that will be filled with the texture width. - * pHeight: A pointer to a uint32 that will be filled with the texture height. + * \sa Refresh_ClaimWindow + * \sa Refresh_Submit + * \sa Refresh_SubmitAndAcquireFence */ REFRESHAPI Refresh_Texture* Refresh_AcquireSwapchainTexture( - Refresh_Device *device, Refresh_CommandBuffer *commandBuffer, - void *windowHandle, - uint32_t *pWidth, - uint32_t *pHeight + SDL_Window *window, + Uint32 *pWidth, + Uint32 *pHeight ); -/* Submits all of the enqueued commands. */ +/** + * Submits a command buffer so its commands can be processed on the GPU. + * It is invalid to use the command buffer after this is called. + * + * \param commandBuffer a command buffer + * + * \sa Refresh_AcquireCommandBuffer + * \sa Refresh_AcquireSwapchainTexture + * \sa Refresh_SubmitAndAcquireFence + */ REFRESHAPI void Refresh_Submit( - Refresh_Device* device, Refresh_CommandBuffer *commandBuffer ); -/* Submits a command buffer and acquires a fence. - * You can use the fence to check if or wait until the command buffer has finished processing. - * You are responsible for releasing this fence when you are done using it. +/** + * Submits a command buffer so its commands can be processed on the GPU, + * and acquires a fence associated with the command buffer. + * You must release this fence when it is no longer needed or it will cause a leak. + * It is invalid to use the command buffer after this is called. + * + * \param commandBuffer a command buffer + * \returns a fence associated with the command buffer + * + * \sa SDL_AcquireCommandBuffer + * \sa Refresh_AcquireSwapchainTexture + * \sa Refresh_Submit + * \sa Refresh_ReleaseFence */ REFRESHAPI Refresh_Fence* Refresh_SubmitAndAcquireFence( - Refresh_Device* device, Refresh_CommandBuffer *commandBuffer ); -/* Waits for the device to become idle. */ +/** + * Blocks the thread until the GPU is completely idle. + * + * \param device a GPU context + * + * \sa Refresh_WaitForFences + */ REFRESHAPI void Refresh_Wait( Refresh_Device *device ); -/* Waits for given fences to be signaled. +/** + * Blocks the thread until the given fences are signaled. * - * waitAll: If 0, waits for any fence to be signaled. If 1, waits for all fences to be signaled. - * fenceCount: The number of fences being submitted. - * pFences: An array of fences to be waited on. + * \param device a GPU context + * \param waitAll if 0, wait for any fence to be signaled, if 1, wait for all fences to be signaled + * \param fenceCount the number of fences in the pFences array + * \param pFences an array of fences to wait on + * + * \sa Refresh_SubmitAndAcquireFence + * \sa Refresh_Wait */ REFRESHAPI void Refresh_WaitForFences( Refresh_Device *device, - uint8_t waitAll, - uint32_t fenceCount, + SDL_bool waitAll, + Uint32 fenceCount, Refresh_Fence **pFences ); -/* Check the status of a fence. 1 means the fence is signaled. */ -REFRESHAPI int Refresh_QueryFence( +/** + * Checks the status of a fence. + * + * \param device a GPU context + * \param fence a fence + * \returns SDL_TRUE if the fence is signaled, SDL_FALSE if it is not + * + * \sa Refresh_SubmitAndAcquireFence + */ +REFRESHAPI SDL_bool Refresh_QueryFence( Refresh_Device *device, Refresh_Fence *fence ); -/* Allows the fence to be reused by future command buffer submissions. - * If you do not release fences after acquiring them, you will cause unbounded resource growth. +/** + * Releases a fence obtained from Refresh_SubmitAndAcquireFence. + * + * \param device a GPU context + * \param fence a fence + * + * \sa Refresh_SubmitAndAcquireFence */ REFRESHAPI void Refresh_ReleaseFence( Refresh_Device *device, Refresh_Fence *fence ); +/* Format Info */ + +/** + * Obtains the texel block size for a texture format. + * + * \param textureFormat the texture format you want to know the texel size of + * \returns the texel block size of the texture format + * + * \sa Refresh_SetTransferData + * \sa Refresh_UploadToTexture + */ +REFRESHAPI Uint32 Refresh_TextureFormatTexelBlockSize( + Refresh_TextureFormat textureFormat +); + +/** + * Determines whether a texture format is supported for a given type and usage. + * + * \param device a GPU context + * \param format the texture format to check + * \param type the type of texture (2D, 3D, Cube) + * \param usage a bitmask of all usage scenarios to check + * \returns whether the texture format is supported for this type and usage + */ +REFRESHAPI SDL_bool Refresh_IsTextureFormatSupported( + Refresh_Device *device, + Refresh_TextureFormat format, + Refresh_TextureType type, + Refresh_TextureUsageFlags usage +); + +/** + * Determines the "best" sample count for a texture format, i.e. + * the highest supported sample count that is <= the desired sample count. + * + * \param device a GPU context + * \param format the texture format to check + * \param desiredSampleCount the sample count you want + * \returns a hardware-specific version of min(preferred, possible) + */ +REFRESHAPI Refresh_SampleCount Refresh_GetBestSampleCount( + Refresh_Device *device, + Refresh_TextureFormat format, + Refresh_SampleCount desiredSampleCount +); + +/* Queries */ + +/** + * Begins an occlusion query. + * + * This function is included for compatibility with old render systems. + * If you are not supporting an old application, do NOT use this. + * + * \param commandBuffer a command buffer + * \param query an occlusion query object + */ +REFRESHAPI void Refresh_OcclusionQueryBegin( + Refresh_CommandBuffer *commandBuffer, + Refresh_OcclusionQuery *query +); + +/** + * Ends an occlusion query. + * You must have called Refresh_OcclusionQueryBegin on the query beforehand. + * + * This function is included for compatibility with old render systems. + * If you are not supporting an old application, do NOT use this. + * + * \param commandBuffer a commandBuffer + * \param query an occlusion query object + * + * \sa Refresh_BeginQuery + */ +REFRESHAPI void Refresh_OcclusionQueryEnd( + Refresh_CommandBuffer *commandBuffer, + Refresh_OcclusionQuery *query +); + +/** + * Checks if an occlusion query is complete and fills in the pixel count of the query. + * + * This function is included for compatibility with old render systems. + * If you are not supporting an old application, do NOT use this. + * + * \param device a GPU context + * \param query an occlusion query object + * \param pixelCount a pointer to be filled with the pixel count + * \returns SDL_TRUE if the occlusion query is complete, SDL_FALSE otherwise + */ +REFRESHAPI SDL_bool Refresh_OcclusionQueryPixelCount( + Refresh_Device *device, + Refresh_OcclusionQuery *query, + Uint32 *pixelCount +); + #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* REFRESH_H */ - -/* vim: set noexpandtab shiftwidth=8 tabstop=8: */ diff --git a/include/Refresh_Image.h b/include/Refresh_image.h similarity index 100% rename from include/Refresh_Image.h rename to include/Refresh_image.h diff --git a/src/Refresh.c b/src/Refresh.c index ec8d7cd..94fef46 100644 --- a/src/Refresh.c +++ b/src/Refresh.c @@ -1,6 +1,6 @@ -/* Refresh - XNA-inspired 3D Graphics Library with modern capabilities +/* Refresh - a cross-platform hardware-accelerated graphics library with modern capabilities * - * Copyright (c) 2020 Evan Hemsley + * Copyright (c) 2020-2024 Evan Hemsley * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from @@ -24,374 +24,417 @@ * */ -#include "Refresh_Driver.h" +#include "Refresh_driver.h" +#include "Refresh_spirv_c.h" -#include +#define NULL_ASSERT(name) SDL_assert(name != NULL); -#define NULL_RETURN(name) if (name == NULL) { return; } -#define NULL_RETURN_NULL(name) if (name == NULL) { return NULL; } +#define CHECK_COMMAND_BUFFER \ + if (commandBuffer == NULL) { return; } \ + if (((CommandBufferCommonHeader*) commandBuffer)->submitted) \ + { \ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Command buffer already submitted!"); \ + return; \ + } + +#define CHECK_COMMAND_BUFFER_RETURN_NULL \ + if (commandBuffer == NULL) { return NULL; } \ + if (((CommandBufferCommonHeader*) commandBuffer)->submitted) \ + { \ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Command buffer already submitted!"); \ + return NULL; \ + } + +#define CHECK_ANY_PASS_IN_PROGRESS \ + if ( \ + ((CommandBufferCommonHeader*) commandBuffer)->renderPass.inProgress || \ + ((CommandBufferCommonHeader*) commandBuffer)->computePass.inProgress || \ + ((CommandBufferCommonHeader*) commandBuffer)->copyPass.inProgress ) \ + { \ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Pass already in progress!"); \ + return NULL; \ + } + +#define CHECK_RENDERPASS \ + if (!((Pass*) renderPass)->inProgress) { \ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Render pass not in progress!"); \ + return; \ + } + +#define CHECK_GRAPHICS_PIPELINE_BOUND \ + if (!((CommandBufferCommonHeader*) RENDERPASS_COMMAND_BUFFER)->graphicsPipelineBound) { \ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Graphics pipeline not bound!"); \ + return; \ + } + +#define CHECK_COMPUTEPASS \ + if (!((Pass*) computePass)->inProgress) { \ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Compute pass not in progress!"); \ + return; \ + } + +#define CHECK_COMPUTE_PIPELINE_BOUND \ + if (!((CommandBufferCommonHeader*) COMPUTEPASS_COMMAND_BUFFER)->computePipelineBound) { \ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Compute pipeline not bound!"); \ + return; \ + } + +#define CHECK_COPYPASS \ + if (!((Pass*) copyPass)->inProgress) { \ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Copy pass not in progress!"); \ + return; \ + } \ + +#define COMMAND_BUFFER_DEVICE \ + ((CommandBufferCommonHeader*) commandBuffer)->device + +#define RENDERPASS_COMMAND_BUFFER \ + ((Pass*) renderPass)->commandBuffer + +#define RENDERPASS_DEVICE \ + ((CommandBufferCommonHeader*) RENDERPASS_COMMAND_BUFFER)->device + +#define COMPUTEPASS_COMMAND_BUFFER \ + ((Pass*) computePass)->commandBuffer + +#define COMPUTEPASS_DEVICE \ + ((CommandBufferCommonHeader*) COMPUTEPASS_COMMAND_BUFFER)->device + +#define COPYPASS_COMMAND_BUFFER \ + ((Pass*) copyPass)->commandBuffer + +#define COPYPASS_DEVICE \ + ((CommandBufferCommonHeader*) COPYPASS_COMMAND_BUFFER)->device /* Drivers */ -#ifdef REFRESH_DRIVER_VULKAN - #define VULKAN_DRIVER &VulkanDriver -#else - #define VULKAN_DRIVER NULL -#endif - -#ifdef REFRESH_DRIVER_PS5 - #define PS5_DRIVER &PS5Driver -#else - #define PS5_DRIVER NULL -#endif - static const Refresh_Driver *backends[] = { - NULL, - VULKAN_DRIVER, - PS5_DRIVER +#if REFRESH_VULKAN + &VulkanDriver, +#endif +#if REFRESH_D3D11 + &D3D11Driver, +#endif +#if REFRESH_METAL + &MetalDriver, +#endif + NULL }; -/* Logging */ - -static void Refresh_Default_LogInfo(const char *msg) -{ - SDL_LogInfo( - SDL_LOG_CATEGORY_APPLICATION, - "%s", - msg - ); -} - -static void Refresh_Default_LogWarn(const char *msg) -{ - SDL_LogWarn( - SDL_LOG_CATEGORY_APPLICATION, - "%s", - msg - ); -} - -static void Refresh_Default_LogError(const char *msg) -{ - SDL_LogError( - SDL_LOG_CATEGORY_APPLICATION, - "%s", - msg - ); -} - -static Refresh_LogFunc Refresh_LogInfoFunc = Refresh_Default_LogInfo; -static Refresh_LogFunc Refresh_LogWarnFunc = Refresh_Default_LogWarn; -static Refresh_LogFunc Refresh_LogErrorFunc = Refresh_Default_LogError; - -#define MAX_MESSAGE_SIZE 1024 - -void Refresh_LogInfo(const char *fmt, ...) -{ - char msg[MAX_MESSAGE_SIZE]; - va_list ap; - va_start(ap, fmt); - SDL_vsnprintf(msg, sizeof(msg), fmt, ap); - va_end(ap); - Refresh_LogInfoFunc(msg); -} - -void Refresh_LogWarn(const char *fmt, ...) -{ - char msg[MAX_MESSAGE_SIZE]; - va_list ap; - va_start(ap, fmt); - SDL_vsnprintf(msg, sizeof(msg), fmt, ap); - va_end(ap); - Refresh_LogWarnFunc(msg); -} - -void Refresh_LogError(const char *fmt, ...) -{ - char msg[MAX_MESSAGE_SIZE]; - va_list ap; - va_start(ap, fmt); - SDL_vsnprintf(msg, sizeof(msg), fmt, ap); - va_end(ap); - Refresh_LogErrorFunc(msg); -} - -#undef MAX_MESSAGE_SIZE - -void Refresh_HookLogFunctions( - Refresh_LogFunc info, - Refresh_LogFunc warn, - Refresh_LogFunc error -) { - Refresh_LogInfoFunc = info; - Refresh_LogWarnFunc = warn; - Refresh_LogErrorFunc = error; -} - -/* Version API */ - -uint32_t Refresh_LinkedVersion(void) -{ - return REFRESH_COMPILED_VERSION; -} - /* Driver Functions */ -static Refresh_Backend selectedBackend = REFRESH_BACKEND_INVALID; - -Refresh_Backend Refresh_SelectBackend(Refresh_Backend preferredBackend, uint32_t *flags) +static Refresh_Backend Refresh_SelectBackend(Refresh_Backend preferredBackends) { - uint32_t i; + Uint32 i; - if (preferredBackend != REFRESH_BACKEND_DONTCARE) + /* Environment override... */ + const char *gpudriver = SDL_GetHint("REFRESH_HINT_BACKEND"); + if (gpudriver != NULL) { - if (backends[preferredBackend] == NULL) + for (i = 0; backends[i]; i += 1) { - Refresh_LogWarn("Preferred backend was not compiled into this binary! Attempting to fall back!"); + if (SDL_strcasecmp(gpudriver, backends[i]->Name) == 0 && backends[i]->PrepareDriver()) + { + return backends[i]->backendflag; + } } - else if (backends[preferredBackend]->PrepareDriver(flags)) + + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "REFRESH_HINT_BACKEND %s unsupported!", gpudriver); + return REFRESH_BACKEND_INVALID; + } + + /* Preferred backends... */ + if (preferredBackends != REFRESH_BACKEND_INVALID) + { + for (i = 0; backends[i]; i += 1) { - selectedBackend = preferredBackend; - return selectedBackend; + if ((preferredBackends & backends[i]->backendflag) && backends[i]->PrepareDriver()) + { + return backends[i]->backendflag; + } + } + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "No preferred Refresh backend found!"); + } + + /* ... Fallback backends */ + for (i = 0; backends[i]; i += 1) + { + if (backends[i]->PrepareDriver()) + { + return backends[i]->backendflag; } } - /* Iterate until we find an appropriate backend. */ - - for (i = 1; i < SDL_arraysize(backends); i += 1) - { - if (i != preferredBackend && backends[i] != NULL && backends[i]->PrepareDriver(flags)) - { - selectedBackend = i; - return i; - } - } - - if (backends[i] == NULL) - { - Refresh_LogError("No supported Refresh backend found!"); - } - - selectedBackend = REFRESH_BACKEND_INVALID; + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No supported Refresh backend found!"); return REFRESH_BACKEND_INVALID; } Refresh_Device* Refresh_CreateDevice( - uint8_t debugMode + Refresh_Backend preferredBackends, + SDL_bool debugMode ) { - if (selectedBackend == REFRESH_BACKEND_INVALID) - { - Refresh_LogError("Invalid backend selection. Did you call Refresh_SelectBackend?"); - return NULL; - } + int i; + Refresh_Device *result = NULL; + Refresh_Backend selectedBackend; - return backends[selectedBackend]->CreateDevice( - debugMode - ); + selectedBackend = Refresh_SelectBackend(preferredBackends); + if (selectedBackend != REFRESH_BACKEND_INVALID) + { + for (i = 0; backends[i]; i += 1) + { + if (backends[i]->backendflag == selectedBackend) + { + result = backends[i]->CreateDevice(debugMode); + if (result != NULL) { + result->backend = backends[i]->backendflag; + break; + } + } + } + } + return result; } void Refresh_DestroyDevice(Refresh_Device *device) { - NULL_RETURN(device); + NULL_ASSERT(device); device->DestroyDevice(device); } -void Refresh_DrawIndexedPrimitives( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - uint32_t baseVertex, - uint32_t startIndex, - uint32_t primitiveCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset -) { - NULL_RETURN(device); - device->DrawIndexedPrimitives( - device->driverData, - commandBuffer, - baseVertex, - startIndex, - primitiveCount, - vertexParamOffset, - fragmentParamOffset - ); +Refresh_Backend Refresh_GetBackend(Refresh_Device *device) +{ + if (device == NULL) { + return REFRESH_BACKEND_INVALID; + } + return device->backend; } -void Refresh_DrawInstancedPrimitives( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - uint32_t baseVertex, - uint32_t startIndex, - uint32_t primitiveCount, - uint32_t instanceCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset +Uint32 Refresh_TextureFormatTexelBlockSize( + Refresh_TextureFormat textureFormat ) { - NULL_RETURN(device); - device->DrawInstancedPrimitives( - device->driverData, - commandBuffer, - baseVertex, - startIndex, - primitiveCount, - instanceCount, - vertexParamOffset, - fragmentParamOffset - ); + switch (textureFormat) + { + case REFRESH_TEXTUREFORMAT_BC1: + return 8; + case REFRESH_TEXTUREFORMAT_BC2: + case REFRESH_TEXTUREFORMAT_BC3: + case REFRESH_TEXTUREFORMAT_BC7: + case REFRESH_TEXTUREFORMAT_BC3_SRGB: + case REFRESH_TEXTUREFORMAT_BC7_SRGB: + return 16; + case REFRESH_TEXTUREFORMAT_R8: + case REFRESH_TEXTUREFORMAT_A8: + case REFRESH_TEXTUREFORMAT_R8_UINT: + return 1; + case REFRESH_TEXTUREFORMAT_R5G6B5: + case REFRESH_TEXTUREFORMAT_B4G4R4A4: + case REFRESH_TEXTUREFORMAT_A1R5G5B5: + case REFRESH_TEXTUREFORMAT_R16_SFLOAT: + case REFRESH_TEXTUREFORMAT_R8G8_SNORM: + case REFRESH_TEXTUREFORMAT_R8G8_UINT: + case REFRESH_TEXTUREFORMAT_R16_UINT: + return 2; + case REFRESH_TEXTUREFORMAT_R8G8B8A8: + case REFRESH_TEXTUREFORMAT_B8G8R8A8: + case REFRESH_TEXTUREFORMAT_R8G8B8A8_SRGB: + case REFRESH_TEXTUREFORMAT_B8G8R8A8_SRGB: + case REFRESH_TEXTUREFORMAT_R32_SFLOAT: + case REFRESH_TEXTUREFORMAT_R16G16_SFLOAT: + case REFRESH_TEXTUREFORMAT_R8G8B8A8_SNORM: + case REFRESH_TEXTUREFORMAT_A2R10G10B10: + case REFRESH_TEXTUREFORMAT_R8G8B8A8_UINT: + case REFRESH_TEXTUREFORMAT_R16G16_UINT: + return 4; + case REFRESH_TEXTUREFORMAT_R16G16B16A16_SFLOAT: + case REFRESH_TEXTUREFORMAT_R16G16B16A16: + case REFRESH_TEXTUREFORMAT_R32G32_SFLOAT: + case REFRESH_TEXTUREFORMAT_R16G16B16A16_UINT: + return 8; + case REFRESH_TEXTUREFORMAT_R32G32B32A32_SFLOAT: + return 16; + default: + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "Unrecognized TextureFormat!" + ); + return 0; + } } -void Refresh_DrawPrimitives( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - uint32_t vertexStart, - uint32_t primitiveCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset +SDL_bool Refresh_IsTextureFormatSupported( + Refresh_Device *device, + Refresh_TextureFormat format, + Refresh_TextureType type, + Refresh_TextureUsageFlags usage ) { - NULL_RETURN(device); - device->DrawPrimitives( - device->driverData, - commandBuffer, - vertexStart, - primitiveCount, - vertexParamOffset, - fragmentParamOffset - ); + if (device == NULL) { return SDL_FALSE; } + return device->IsTextureFormatSupported( + device->driverData, + format, + type, + usage + ); } -void Refresh_DrawPrimitivesIndirect( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint32_t offsetInBytes, - uint32_t drawCount, - uint32_t stride, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset +Refresh_SampleCount Refresh_GetBestSampleCount( + Refresh_Device* device, + Refresh_TextureFormat format, + Refresh_SampleCount desiredSampleCount ) { - NULL_RETURN(device); - device->DrawPrimitivesIndirect( - device->driverData, - commandBuffer, - buffer, - offsetInBytes, - drawCount, - stride, - vertexParamOffset, - fragmentParamOffset - ); + if (device == NULL) { return 0; } + return device->GetBestSampleCount( + device->driverData, + format, + desiredSampleCount + ); } -void Refresh_DispatchCompute( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - uint32_t groupCountX, - uint32_t groupCountY, - uint32_t groupCountZ, - uint32_t computeParamOffset -) { - NULL_RETURN(device); - device->DispatchCompute( - device->driverData, - commandBuffer, - groupCountX, - groupCountY, - groupCountZ, - computeParamOffset - ); -} +/* State Creation */ Refresh_ComputePipeline* Refresh_CreateComputePipeline( Refresh_Device *device, - Refresh_ComputeShaderInfo *computeShaderInfo + Refresh_ComputePipelineCreateInfo *computePipelineCreateInfo ) { - NULL_RETURN_NULL(device); + NULL_ASSERT(device) return device->CreateComputePipeline( device->driverData, - computeShaderInfo + computePipelineCreateInfo ); } Refresh_GraphicsPipeline* Refresh_CreateGraphicsPipeline( Refresh_Device *device, - Refresh_GraphicsPipelineCreateInfo *pipelineCreateInfo + Refresh_GraphicsPipelineCreateInfo *graphicsPipelineCreateInfo ) { - NULL_RETURN_NULL(device); + Refresh_TextureFormat newFormat; + + NULL_ASSERT(device) + + /* Automatically swap out the depth format if it's unsupported. + * See Refresh_CreateTexture. + */ + if ( + graphicsPipelineCreateInfo->attachmentInfo.hasDepthStencilAttachment && + !device->IsTextureFormatSupported( + device->driverData, + graphicsPipelineCreateInfo->attachmentInfo.depthStencilFormat, + REFRESH_TEXTURETYPE_2D, + REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT + ) + ) { + switch (graphicsPipelineCreateInfo->attachmentInfo.depthStencilFormat) + { + case REFRESH_TEXTUREFORMAT_D24_UNORM: + newFormat = REFRESH_TEXTUREFORMAT_D32_SFLOAT; + break; + case REFRESH_TEXTUREFORMAT_D32_SFLOAT: + newFormat = REFRESH_TEXTUREFORMAT_D24_UNORM; + break; + case REFRESH_TEXTUREFORMAT_D24_UNORM_S8_UINT: + newFormat = REFRESH_TEXTUREFORMAT_D32_SFLOAT_S8_UINT; + break; + case REFRESH_TEXTUREFORMAT_D32_SFLOAT_S8_UINT: + newFormat = REFRESH_TEXTUREFORMAT_D24_UNORM_S8_UINT; + break; + default: + /* This should never happen, but just in case... */ + newFormat = REFRESH_TEXTUREFORMAT_D16_UNORM; + break; + } + + SDL_LogWarn( + SDL_LOG_CATEGORY_APPLICATION, + "Requested unsupported depth format %d, falling back to format %d!", + graphicsPipelineCreateInfo->attachmentInfo.depthStencilFormat, + newFormat + ); + graphicsPipelineCreateInfo->attachmentInfo.depthStencilFormat = newFormat; + } + return device->CreateGraphicsPipeline( device->driverData, - pipelineCreateInfo + graphicsPipelineCreateInfo ); } Refresh_Sampler* Refresh_CreateSampler( Refresh_Device *device, - Refresh_SamplerStateCreateInfo *samplerStateCreateInfo + Refresh_SamplerCreateInfo *samplerStateInfo ) { - NULL_RETURN_NULL(device); + NULL_ASSERT(device) return device->CreateSampler( device->driverData, - samplerStateCreateInfo + samplerStateInfo ); } -Refresh_ShaderModule* Refresh_CreateShaderModule( - Refresh_Device *device, - Refresh_ShaderModuleCreateInfo *shaderModuleCreateInfo +Refresh_Shader* Refresh_CreateShader( + Refresh_Device *device, + Refresh_ShaderCreateInfo *shaderCreateInfo ) { - Refresh_ShaderModuleCreateInfo driverSpecificCreateInfo = { 0, NULL }; - uint8_t *bytes; - uint32_t i, size; - - NULL_RETURN_NULL(device); - - /* verify the magic number in the shader blob header */ - bytes = (uint8_t*) shaderModuleCreateInfo->byteCode; - if (bytes[0] != 'R' || bytes[1] != 'F' || bytes[2] != 'S' || bytes[3] != 'H') - { - Refresh_LogError("Cannot parse malformed Refresh shader blob!"); - return NULL; - } - - /* find the code for the selected backend */ - i = 4; - while (i < shaderModuleCreateInfo->codeSize) - { - size = *((uint32_t*) &bytes[i + 1]); - - if (bytes[i] == (uint8_t) selectedBackend) - { - driverSpecificCreateInfo.codeSize = size; - driverSpecificCreateInfo.byteCode = (uint32_t*) &bytes[i + 1 + sizeof(uint32_t)]; - break; - } - else - { - /* skip over the backend byte, the blob size, and the blob */ - i += 1 + sizeof(uint32_t) + size; - } - } - - /* verify the shader blob supports the selected backend */ - if (driverSpecificCreateInfo.byteCode == NULL) - { - Refresh_LogError( - "Cannot create shader module that does not contain shader code for the selected backend! " - "Recompile your shader and enable this backend." - ); - return NULL; - } - - return device->CreateShaderModule( - device->driverData, - &driverSpecificCreateInfo - ); + if (shaderCreateInfo->format == REFRESH_SHADERFORMAT_SPIRV && + device->backend != REFRESH_BACKEND_VULKAN) { + return SDL_CreateShaderFromSPIRV(device, shaderCreateInfo); + } + return device->CreateShader( + device->driverData, + shaderCreateInfo + ); } Refresh_Texture* Refresh_CreateTexture( Refresh_Device *device, Refresh_TextureCreateInfo *textureCreateInfo ) { - NULL_RETURN_NULL(device); + Refresh_TextureFormat newFormat; + + NULL_ASSERT(device) + + /* Automatically swap out the depth format if it's unsupported. + * All backends have universal support for D16. + * Vulkan always supports at least one of { D24, D32 } and one of { D24_S8, D32_S8 }. + * D3D11 always supports all depth formats. + * Metal always supports D32 and D32_S8. + * So if D32/_S8 is not supported, we can safely fall back to D24/_S8, and vice versa. + */ + if (IsDepthFormat(textureCreateInfo->format)) + { + if (!device->IsTextureFormatSupported( + device->driverData, + textureCreateInfo->format, + REFRESH_TEXTURETYPE_2D, /* assuming that driver support for 2D implies support for Cube */ + textureCreateInfo->usageFlags) + ) { + switch (textureCreateInfo->format) + { + case REFRESH_TEXTUREFORMAT_D24_UNORM: + newFormat = REFRESH_TEXTUREFORMAT_D32_SFLOAT; + break; + case REFRESH_TEXTUREFORMAT_D32_SFLOAT: + newFormat = REFRESH_TEXTUREFORMAT_D24_UNORM; + break; + case REFRESH_TEXTUREFORMAT_D24_UNORM_S8_UINT: + newFormat = REFRESH_TEXTUREFORMAT_D32_SFLOAT_S8_UINT; + break; + case REFRESH_TEXTUREFORMAT_D32_SFLOAT_S8_UINT: + newFormat = REFRESH_TEXTUREFORMAT_D24_UNORM_S8_UINT; + break; + default: + /* This should never happen, but just in case... */ + newFormat = REFRESH_TEXTUREFORMAT_D16_UNORM; + break; + } + + SDL_LogWarn( + SDL_LOG_CATEGORY_APPLICATION, + "Requested unsupported depth format %d, falling back to format %d!", + textureCreateInfo->format, + newFormat + ); + textureCreateInfo->format = newFormat; + } + } + return device->CreateTexture( device->driverData, textureCreateInfo @@ -401,9 +444,9 @@ Refresh_Texture* Refresh_CreateTexture( Refresh_Buffer* Refresh_CreateBuffer( Refresh_Device *device, Refresh_BufferUsageFlags usageFlags, - uint32_t sizeInBytes + Uint32 sizeInBytes ) { - NULL_RETURN_NULL(device); + NULL_ASSERT(device) return device->CreateBuffer( device->driverData, usageFlags, @@ -411,503 +454,994 @@ Refresh_Buffer* Refresh_CreateBuffer( ); } -void Refresh_SetTextureData( +Refresh_TransferBuffer* Refresh_CreateTransferBuffer( Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *textureSlice, - void *data, - uint32_t dataLengthInBytes + Refresh_TransferUsage usage, + Refresh_TransferBufferMapFlags mapFlags, + Uint32 sizeInBytes ) { - NULL_RETURN(device); - device->SetTextureData( + NULL_ASSERT(device) + return device->CreateTransferBuffer( device->driverData, - commandBuffer, - textureSlice, - data, - dataLengthInBytes + usage, + mapFlags, + sizeInBytes ); } -void Refresh_SetTextureDataYUV( - Refresh_Device *device, - Refresh_CommandBuffer* commandBuffer, - Refresh_Texture *y, - Refresh_Texture *u, - Refresh_Texture *v, - uint32_t yWidth, - uint32_t yHeight, - uint32_t uvWidth, - uint32_t uvHeight, - void *yDataPtr, - void *uDataPtr, - void *vDataPtr, - uint32_t yDataLength, - uint32_t uvDataLength, - uint32_t yStride, - uint32_t uvStride +Refresh_OcclusionQuery* Refresh_CreateOcclusionQuery( + Refresh_Device *device ) { - NULL_RETURN(device); - device->SetTextureDataYUV( - device->driverData, - commandBuffer, - y, - u, - v, - yWidth, - yHeight, - uvWidth, - uvHeight, - yDataPtr, - uDataPtr, - vDataPtr, - yDataLength, - uvDataLength, - yStride, - uvStride - ); + NULL_ASSERT(device) + return device->CreateOcclusionQuery( + device->driverData + ); } -void Refresh_CopyTextureToTexture( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *sourceTextureSlice, - Refresh_TextureSlice *destinationTextureSlice, - Refresh_Filter filter -) { - NULL_RETURN(device); - device->CopyTextureToTexture( - device->driverData, - commandBuffer, - sourceTextureSlice, - destinationTextureSlice, - filter - ); -} +/* Debug Naming */ -void Refresh_CopyTextureToBuffer( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *textureSlice, - Refresh_Buffer *buffer -) { - NULL_RETURN(device); - device->CopyTextureToBuffer( - device->driverData, - commandBuffer, - textureSlice, - buffer - ); -} - -void Refresh_SetBufferData( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint32_t offsetInBytes, - void* data, - uint32_t dataLength -) { - NULL_RETURN(device); - device->SetBufferData( - device->driverData, - commandBuffer, - buffer, - offsetInBytes, - data, - dataLength - ); -} - -uint32_t Refresh_PushVertexShaderUniforms( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - void *data, - uint32_t dataLengthInBytes -) { - if (device == NULL) { return 0; } - return device->PushVertexShaderUniforms( - device->driverData, - commandBuffer, - data, - dataLengthInBytes - ); -} - -uint32_t Refresh_PushFragmentShaderUniforms( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - void *data, - uint32_t dataLengthInBytes -) { - if (device == NULL) { return 0; } - return device->PushFragmentShaderUniforms( - device->driverData, - commandBuffer, - data, - dataLengthInBytes - ); -} - -uint32_t Refresh_PushComputeShaderUniforms( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - void *data, - uint32_t dataLengthInBytes -) { - if (device == NULL) { return 0; } - return device->PushComputeShaderUniforms( - device->driverData, - commandBuffer, - data, - dataLengthInBytes - ); -} - -void Refresh_BindVertexSamplers( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures, - Refresh_Sampler **pSamplers -) { - NULL_RETURN(device); - device->BindVertexSamplers( - device->driverData, - commandBuffer, - pTextures, - pSamplers - ); -} - -void Refresh_BindFragmentSamplers( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures, - Refresh_Sampler **pSamplers -) { - NULL_RETURN(device); - device->BindFragmentSamplers( - device->driverData, - commandBuffer, - pTextures, - pSamplers - ); -} - -void Refresh_GetBufferData( +void Refresh_SetBufferName( Refresh_Device *device, Refresh_Buffer *buffer, - void *data, - uint32_t dataLengthInBytes + const char *text ) { - NULL_RETURN(device); - device->GetBufferData( + NULL_ASSERT(device) + NULL_ASSERT(buffer) + + device->SetBufferName( device->driverData, buffer, - data, - dataLengthInBytes + text ); } -void Refresh_QueueDestroyTexture( +void Refresh_SetTextureName( + Refresh_Device *device, + Refresh_Texture *texture, + const char *text +) { + NULL_ASSERT(device) + NULL_ASSERT(texture) + + device->SetTextureName( + device->driverData, + texture, + text + ); +} + +void Refresh_SetStringMarker( + Refresh_CommandBuffer *commandBuffer, + const char *text +) { + CHECK_COMMAND_BUFFER + COMMAND_BUFFER_DEVICE->SetStringMarker( + commandBuffer, + text + ); +} + +/* Disposal */ + +void Refresh_ReleaseTexture( Refresh_Device *device, Refresh_Texture *texture ) { - NULL_RETURN(device); - device->QueueDestroyTexture( + NULL_ASSERT(device); + device->ReleaseTexture( device->driverData, texture ); } -void Refresh_QueueDestroySampler( +void Refresh_ReleaseSampler( Refresh_Device *device, Refresh_Sampler *sampler ) { - NULL_RETURN(device); - device->QueueDestroySampler( + NULL_ASSERT(device); + device->ReleaseSampler( device->driverData, sampler ); } -void Refresh_QueueDestroyBuffer( +void Refresh_ReleaseBuffer( Refresh_Device *device, Refresh_Buffer *buffer ) { - NULL_RETURN(device); - device->QueueDestroyBuffer( + NULL_ASSERT(device); + device->ReleaseBuffer( device->driverData, buffer ); } -void Refresh_QueueDestroyShaderModule( +void Refresh_ReleaseTransferBuffer( Refresh_Device *device, - Refresh_ShaderModule *shaderModule + Refresh_TransferBuffer *transferBuffer ) { - NULL_RETURN(device); - device->QueueDestroyShaderModule( + NULL_ASSERT(device); + device->ReleaseTransferBuffer( device->driverData, - shaderModule + transferBuffer ); } -void Refresh_QueueDestroyComputePipeline( +void Refresh_ReleaseShader( + Refresh_Device *device, + Refresh_Shader *shader +) { + NULL_ASSERT(device); + device->ReleaseShader( + device->driverData, + shader + ); +} + +void Refresh_ReleaseComputePipeline( Refresh_Device *device, Refresh_ComputePipeline *computePipeline ) { - NULL_RETURN(device); - device->QueueDestroyComputePipeline( + NULL_ASSERT(device); + device->ReleaseComputePipeline( device->driverData, computePipeline ); } -void Refresh_QueueDestroyGraphicsPipeline( +void Refresh_ReleaseGraphicsPipeline( Refresh_Device *device, Refresh_GraphicsPipeline *graphicsPipeline ) { - NULL_RETURN(device); - device->QueueDestroyGraphicsPipeline( + NULL_ASSERT(device); + device->ReleaseGraphicsPipeline( device->driverData, graphicsPipeline ); } -void Refresh_BeginRenderPass( - Refresh_Device *device, +void Refresh_ReleaseOcclusionQuery( + Refresh_Device *device, + Refresh_OcclusionQuery *query +) { + NULL_ASSERT(device); + device->ReleaseOcclusionQuery( + device->driverData, + query + ); +} + +/* Render Pass */ + +Refresh_RenderPass* Refresh_BeginRenderPass( Refresh_CommandBuffer *commandBuffer, Refresh_ColorAttachmentInfo *colorAttachmentInfos, - uint32_t colorAttachmentCount, + Uint32 colorAttachmentCount, Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo ) { - NULL_RETURN(device); - device->BeginRenderPass( - device->driverData, + CommandBufferCommonHeader *commandBufferHeader; + + CHECK_COMMAND_BUFFER_RETURN_NULL + CHECK_ANY_PASS_IN_PROGRESS + COMMAND_BUFFER_DEVICE->BeginRenderPass( commandBuffer, colorAttachmentInfos, colorAttachmentCount, depthStencilAttachmentInfo ); + + commandBufferHeader = (CommandBufferCommonHeader*) commandBuffer; + commandBufferHeader->renderPass.inProgress = SDL_TRUE; + return (Refresh_RenderPass*) &(commandBufferHeader->renderPass); } -void Refresh_EndRenderPass( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer +void Refresh_BindGraphicsPipeline( + Refresh_RenderPass *renderPass, + Refresh_GraphicsPipeline *graphicsPipeline ) { - NULL_RETURN(device); - device->EndRenderPass( - device->driverData, - commandBuffer + CommandBufferCommonHeader *commandBufferHeader; + + NULL_ASSERT(renderPass) + RENDERPASS_DEVICE->BindGraphicsPipeline( + RENDERPASS_COMMAND_BUFFER, + graphicsPipeline ); + + commandBufferHeader = (CommandBufferCommonHeader*) RENDERPASS_COMMAND_BUFFER; + commandBufferHeader->graphicsPipelineBound = SDL_TRUE; } void Refresh_SetViewport( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, + Refresh_RenderPass *renderPass, Refresh_Viewport *viewport ) { - NULL_RETURN(device) - device->SetViewport( - device->driverData, - commandBuffer, + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + RENDERPASS_DEVICE->SetViewport( + RENDERPASS_COMMAND_BUFFER, viewport ); } void Refresh_SetScissor( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, + Refresh_RenderPass *renderPass, Refresh_Rect *scissor ) { - NULL_RETURN(device) - device->SetScissor( - device->driverData, - commandBuffer, + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + RENDERPASS_DEVICE->SetScissor( + RENDERPASS_COMMAND_BUFFER, scissor ); } -void Refresh_BindGraphicsPipeline( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_GraphicsPipeline *graphicsPipeline -) { - NULL_RETURN(device); - device->BindGraphicsPipeline( - device->driverData, - commandBuffer, - graphicsPipeline - ); -} - void Refresh_BindVertexBuffers( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - uint32_t firstBinding, - uint32_t bindingCount, - Refresh_Buffer **pBuffers, - uint64_t *pOffsets + Refresh_RenderPass *renderPass, + Uint32 firstBinding, + Refresh_BufferBinding *pBindings, + Uint32 bindingCount ) { - NULL_RETURN(device); - device->BindVertexBuffers( - device->driverData, - commandBuffer, + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->BindVertexBuffers( + RENDERPASS_COMMAND_BUFFER, firstBinding, - bindingCount, - pBuffers, - pOffsets + pBindings, + bindingCount ); } void Refresh_BindIndexBuffer( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint64_t offset, + Refresh_RenderPass *renderPass, + Refresh_BufferBinding *pBinding, Refresh_IndexElementSize indexElementSize ) { - NULL_RETURN(device); - device->BindIndexBuffer( - device->driverData, - commandBuffer, - buffer, - offset, + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->BindIndexBuffer( + RENDERPASS_COMMAND_BUFFER, + pBinding, indexElementSize ); } -void Refresh_BindComputePipeline( - Refresh_Device *device, +void Refresh_BindVertexSamplers( + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_TextureSamplerBinding *textureSamplerBindings, + Uint32 bindingCount +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->BindVertexSamplers( + RENDERPASS_COMMAND_BUFFER, + firstSlot, + textureSamplerBindings, + bindingCount + ); +} + +void Refresh_BindVertexStorageTextures( + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->BindVertexStorageTextures( + RENDERPASS_COMMAND_BUFFER, + firstSlot, + storageTextureSlices, + bindingCount + ); +} + +void Refresh_BindVertexStorageBuffers( + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->BindVertexStorageBuffers( + RENDERPASS_COMMAND_BUFFER, + firstSlot, + storageBuffers, + bindingCount + ); +} + +void Refresh_BindFragmentSamplers( + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_TextureSamplerBinding *textureSamplerBindings, + Uint32 bindingCount +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->BindFragmentSamplers( + RENDERPASS_COMMAND_BUFFER, + firstSlot, + textureSamplerBindings, + bindingCount + ); +} + +void Refresh_BindFragmentStorageTextures( + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->BindFragmentStorageTextures( + RENDERPASS_COMMAND_BUFFER, + firstSlot, + storageTextureSlices, + bindingCount + ); +} + +void Refresh_BindFragmentStorageBuffers( + Refresh_RenderPass *renderPass, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->BindFragmentStorageBuffers( + RENDERPASS_COMMAND_BUFFER, + firstSlot, + storageBuffers, + bindingCount + ); +} + +void Refresh_PushVertexUniformData( + Refresh_RenderPass *renderPass, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->PushVertexUniformData( + RENDERPASS_COMMAND_BUFFER, + slotIndex, + data, + dataLengthInBytes + ); +} + +void Refresh_PushFragmentUniformData( + Refresh_RenderPass *renderPass, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->PushFragmentUniformData( + RENDERPASS_COMMAND_BUFFER, + slotIndex, + data, + dataLengthInBytes + ); +} + +void Refresh_DrawIndexedPrimitives( + Refresh_RenderPass *renderPass, + Uint32 baseVertex, + Uint32 startIndex, + Uint32 primitiveCount, + Uint32 instanceCount +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->DrawIndexedPrimitives( + RENDERPASS_COMMAND_BUFFER, + baseVertex, + startIndex, + primitiveCount, + instanceCount + ); +} + +void Refresh_DrawPrimitives( + Refresh_RenderPass *renderPass, + Uint32 vertexStart, + Uint32 primitiveCount +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->DrawPrimitives( + RENDERPASS_COMMAND_BUFFER, + vertexStart, + primitiveCount + ); +} + +void Refresh_DrawPrimitivesIndirect( + Refresh_RenderPass *renderPass, + Refresh_Buffer *buffer, + Uint32 offsetInBytes, + Uint32 drawCount, + Uint32 stride +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->DrawPrimitivesIndirect( + RENDERPASS_COMMAND_BUFFER, + buffer, + offsetInBytes, + drawCount, + stride + ); +} + +void Refresh_DrawIndexedPrimitivesIndirect( + Refresh_RenderPass *renderPass, + Refresh_Buffer *buffer, + Uint32 offsetInBytes, + Uint32 drawCount, + Uint32 stride +) { + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + CHECK_GRAPHICS_PIPELINE_BOUND + RENDERPASS_DEVICE->DrawIndexedPrimitivesIndirect( + RENDERPASS_COMMAND_BUFFER, + buffer, + offsetInBytes, + drawCount, + stride + ); +} + +void Refresh_EndRenderPass( + Refresh_RenderPass *renderPass +) { + CommandBufferCommonHeader *commandBufferCommonHeader; + + NULL_ASSERT(renderPass) + CHECK_RENDERPASS + RENDERPASS_DEVICE->EndRenderPass( + RENDERPASS_COMMAND_BUFFER + ); + + commandBufferCommonHeader = (CommandBufferCommonHeader*) RENDERPASS_COMMAND_BUFFER; + commandBufferCommonHeader->renderPass.inProgress = SDL_FALSE; + commandBufferCommonHeader->graphicsPipelineBound = SDL_FALSE; +} + +/* Compute Pass */ + +Refresh_ComputePass* Refresh_BeginComputePass( Refresh_CommandBuffer *commandBuffer, + Refresh_StorageTextureReadWriteBinding *storageTextureBindings, + Uint32 storageTextureBindingCount, + Refresh_StorageBufferReadWriteBinding *storageBufferBindings, + Uint32 storageBufferBindingCount +) { + CommandBufferCommonHeader* commandBufferHeader; + + CHECK_COMMAND_BUFFER_RETURN_NULL + CHECK_ANY_PASS_IN_PROGRESS + COMMAND_BUFFER_DEVICE->BeginComputePass( + commandBuffer, + storageTextureBindings, + storageTextureBindingCount, + storageBufferBindings, + storageBufferBindingCount + ); + + commandBufferHeader = (CommandBufferCommonHeader*) commandBuffer; + commandBufferHeader->computePass.inProgress = SDL_TRUE; + return (Refresh_ComputePass*) &(commandBufferHeader->computePass); +} + +void Refresh_BindComputePipeline( + Refresh_ComputePass *computePass, Refresh_ComputePipeline *computePipeline ) { - NULL_RETURN(device); - device->BindComputePipeline( - device->driverData, - commandBuffer, + CommandBufferCommonHeader* commandBufferHeader; + + NULL_ASSERT(computePass) + CHECK_COMPUTEPASS + COMPUTEPASS_DEVICE->BindComputePipeline( + COMPUTEPASS_COMMAND_BUFFER, computePipeline ); + + commandBufferHeader = (CommandBufferCommonHeader*) COMPUTEPASS_COMMAND_BUFFER; + commandBufferHeader->computePipelineBound = SDL_TRUE; + } -void Refresh_BindComputeBuffers( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer **pBuffers +void Refresh_BindComputeStorageTextures( + Refresh_ComputePass *computePass, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount ) { - NULL_RETURN(device); - device->BindComputeBuffers( - device->driverData, - commandBuffer, - pBuffers + NULL_ASSERT(computePass) + CHECK_COMPUTEPASS + CHECK_COMPUTE_PIPELINE_BOUND + COMPUTEPASS_DEVICE->BindComputeStorageTextures( + COMPUTEPASS_COMMAND_BUFFER, + firstSlot, + storageTextureSlices, + bindingCount + ); +} + +void Refresh_BindComputeStorageBuffers( + Refresh_ComputePass *computePass, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +) { + NULL_ASSERT(computePass) + CHECK_COMPUTEPASS + CHECK_COMPUTE_PIPELINE_BOUND + COMPUTEPASS_DEVICE->BindComputeStorageBuffers( + COMPUTEPASS_COMMAND_BUFFER, + firstSlot, + storageBuffers, + bindingCount + ); +} + +void Refresh_PushComputeUniformData( + Refresh_ComputePass *computePass, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +) { + NULL_ASSERT(computePass) + CHECK_COMPUTEPASS + CHECK_COMPUTE_PIPELINE_BOUND + COMPUTEPASS_DEVICE->PushComputeUniformData( + COMPUTEPASS_COMMAND_BUFFER, + slotIndex, + data, + dataLengthInBytes ); } -void Refresh_BindComputeTextures( - Refresh_Device *device, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures +void Refresh_DispatchCompute( + Refresh_ComputePass *computePass, + Uint32 groupCountX, + Uint32 groupCountY, + Uint32 groupCountZ ) { - NULL_RETURN(device); - device->BindComputeTextures( - device->driverData, - commandBuffer, - pTextures + NULL_ASSERT(computePass) + CHECK_COMPUTEPASS + CHECK_COMPUTE_PIPELINE_BOUND + COMPUTEPASS_DEVICE->DispatchCompute( + COMPUTEPASS_COMMAND_BUFFER, + groupCountX, + groupCountY, + groupCountZ ); } -uint8_t Refresh_ClaimWindow( +void Refresh_EndComputePass( + Refresh_ComputePass *computePass +) { + CommandBufferCommonHeader* commandBufferCommonHeader; + + NULL_ASSERT(computePass) + CHECK_COMPUTEPASS + COMPUTEPASS_DEVICE->EndComputePass( + COMPUTEPASS_COMMAND_BUFFER + ); + + commandBufferCommonHeader = (CommandBufferCommonHeader*) COMPUTEPASS_COMMAND_BUFFER; + commandBufferCommonHeader->computePass.inProgress = SDL_FALSE; + commandBufferCommonHeader->computePipelineBound = SDL_FALSE; +} + +/* TransferBuffer Data */ + +void Refresh_MapTransferBuffer( + Refresh_Device *device, + Refresh_TransferBuffer *transferBuffer, + SDL_bool cycle, + void **ppData +) { + NULL_ASSERT(device) + NULL_ASSERT(transferBuffer) + device->MapTransferBuffer( + device->driverData, + transferBuffer, + cycle, + ppData + ); +} + +void Refresh_UnmapTransferBuffer( + Refresh_Device *device, + Refresh_TransferBuffer *transferBuffer +) { + NULL_ASSERT(device) + NULL_ASSERT(transferBuffer) + device->UnmapTransferBuffer( + device->driverData, + transferBuffer + ); +} + +void Refresh_SetTransferData( Refresh_Device *device, - void *windowHandle, + void* data, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +) { + NULL_ASSERT(device) + device->SetTransferData( + device->driverData, + data, + transferBuffer, + copyParams, + cycle + ); +} + +void Refresh_GetTransferData( + Refresh_Device *device, + Refresh_TransferBuffer *transferBuffer, + void* data, + Refresh_BufferCopy *copyParams +) { + NULL_ASSERT(device) + device->GetTransferData( + device->driverData, + transferBuffer, + data, + copyParams + ); +} + +/* Copy Pass */ + +Refresh_CopyPass* Refresh_BeginCopyPass( + Refresh_CommandBuffer *commandBuffer +) { + CommandBufferCommonHeader *commandBufferHeader; + + CHECK_COMMAND_BUFFER_RETURN_NULL + CHECK_ANY_PASS_IN_PROGRESS + COMMAND_BUFFER_DEVICE->BeginCopyPass( + commandBuffer + ); + + commandBufferHeader = (CommandBufferCommonHeader*) commandBuffer; + commandBufferHeader->copyPass.inProgress = SDL_TRUE; + return (Refresh_CopyPass*) &(commandBufferHeader->copyPass); +} + +void Refresh_UploadToTexture( + Refresh_CopyPass *copyPass, + Refresh_TransferBuffer *transferBuffer, + Refresh_TextureRegion *textureRegion, + Refresh_BufferImageCopy *copyParams, + SDL_bool cycle +) { + NULL_ASSERT(copyPass) + CHECK_COPYPASS + COPYPASS_DEVICE->UploadToTexture( + COPYPASS_COMMAND_BUFFER, + transferBuffer, + textureRegion, + copyParams, + cycle + ); +} + +void Refresh_UploadToBuffer( + Refresh_CopyPass *copyPass, + Refresh_TransferBuffer *transferBuffer, + Refresh_Buffer *buffer, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +) { + NULL_ASSERT(copyPass) + COPYPASS_DEVICE->UploadToBuffer( + COPYPASS_COMMAND_BUFFER, + transferBuffer, + buffer, + copyParams, + cycle + ); +} + +void Refresh_CopyTextureToTexture( + Refresh_CopyPass *copyPass, + Refresh_TextureRegion *source, + Refresh_TextureRegion *destination, + SDL_bool cycle +) { + NULL_ASSERT(copyPass) + COPYPASS_DEVICE->CopyTextureToTexture( + COPYPASS_COMMAND_BUFFER, + source, + destination, + cycle + ); +} + +void Refresh_CopyBufferToBuffer( + Refresh_CopyPass *copyPass, + Refresh_Buffer *source, + Refresh_Buffer *destination, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +) { + NULL_ASSERT(copyPass) + COPYPASS_DEVICE->CopyBufferToBuffer( + COPYPASS_COMMAND_BUFFER, + source, + destination, + copyParams, + cycle + ); +} + +void Refresh_GenerateMipmaps( + Refresh_CopyPass *copyPass, + Refresh_Texture *texture +) { + NULL_ASSERT(copyPass) + COPYPASS_DEVICE->GenerateMipmaps( + COPYPASS_COMMAND_BUFFER, + texture + ); +} + +void Refresh_DownloadFromTexture( + Refresh_CopyPass *copyPass, + Refresh_TextureRegion *textureRegion, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferImageCopy *copyParams +) { + NULL_ASSERT(copyPass); + COPYPASS_DEVICE->DownloadFromTexture( + COPYPASS_COMMAND_BUFFER, + textureRegion, + transferBuffer, + copyParams + ); +} + +void Refresh_DownloadFromBuffer( + Refresh_CopyPass *copyPass, + Refresh_Buffer *buffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams +) { + NULL_ASSERT(copyPass); + COPYPASS_DEVICE->DownloadFromBuffer( + COPYPASS_COMMAND_BUFFER, + buffer, + transferBuffer, + copyParams + ); +} + +void Refresh_EndCopyPass( + Refresh_CopyPass *copyPass +) { + NULL_ASSERT(copyPass) + CHECK_COPYPASS + COPYPASS_DEVICE->EndCopyPass( + COPYPASS_COMMAND_BUFFER + ); + + ((CommandBufferCommonHeader*) COPYPASS_COMMAND_BUFFER)->copyPass.inProgress = SDL_FALSE; +} + +void Refresh_Blit( + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *source, + Refresh_TextureRegion *destination, + Refresh_Filter filterMode, + SDL_bool cycle +) { + CHECK_COMMAND_BUFFER + COMMAND_BUFFER_DEVICE->Blit( + commandBuffer, + source, + destination, + filterMode, + cycle + ); +} + +/* Submission/Presentation */ + +SDL_bool Refresh_SupportsSwapchainComposition( + Refresh_Device *device, + SDL_Window *window, + Refresh_SwapchainComposition swapchainFormat +) { + if (device == NULL) { return 0; } + return device->SupportsSwapchainComposition( + device->driverData, + window, + swapchainFormat + ); +} + +SDL_bool Refresh_SupportsPresentMode( + Refresh_Device *device, + SDL_Window *window, Refresh_PresentMode presentMode +) { + if (device == NULL) { return 0; } + return device->SupportsPresentMode( + device->driverData, + window, + presentMode + ); +} + +SDL_bool Refresh_ClaimWindow( + Refresh_Device *device, + SDL_Window *window, + Refresh_SwapchainComposition swapchainFormat, + Refresh_PresentMode presentMode ) { if (device == NULL) { return 0; } return device->ClaimWindow( device->driverData, - windowHandle, - presentMode + window, + swapchainFormat, + presentMode ); } void Refresh_UnclaimWindow( Refresh_Device *device, - void *windowHandle + SDL_Window *window ) { - NULL_RETURN(device); + NULL_ASSERT(device); device->UnclaimWindow( device->driverData, - windowHandle + window + ); +} + +void Refresh_SetSwapchainParameters( + Refresh_Device *device, + SDL_Window *window, + Refresh_SwapchainComposition swapchainFormat, + Refresh_PresentMode presentMode +) { + NULL_ASSERT(device); + device->SetSwapchainParameters( + device->driverData, + window, + swapchainFormat, + presentMode + ); +} + +Refresh_TextureFormat Refresh_GetSwapchainTextureFormat( + Refresh_Device *device, + SDL_Window *window +) { + if (device == NULL) { return 0; } + return device->GetSwapchainTextureFormat( + device->driverData, + window ); } Refresh_CommandBuffer* Refresh_AcquireCommandBuffer( Refresh_Device *device ) { - NULL_RETURN_NULL(device); - return device->AcquireCommandBuffer( + Refresh_CommandBuffer* commandBuffer; + CommandBufferCommonHeader *commandBufferHeader; + NULL_ASSERT(device); + + commandBuffer = device->AcquireCommandBuffer( device->driverData ); + + if (commandBuffer == NULL) + { + return NULL; + } + + commandBufferHeader = (CommandBufferCommonHeader*) commandBuffer; + commandBufferHeader->device = device; + commandBufferHeader->renderPass.commandBuffer = commandBuffer; + commandBufferHeader->renderPass.inProgress = SDL_FALSE; + commandBufferHeader->graphicsPipelineBound = SDL_FALSE; + commandBufferHeader->computePass.commandBuffer = commandBuffer; + commandBufferHeader->computePass.inProgress = SDL_FALSE; + commandBufferHeader->computePipelineBound = SDL_FALSE; + commandBufferHeader->copyPass.commandBuffer = commandBuffer; + commandBufferHeader->copyPass.inProgress = SDL_FALSE; + commandBufferHeader->submitted = SDL_FALSE; + + return commandBuffer; } Refresh_Texture* Refresh_AcquireSwapchainTexture( - Refresh_Device *device, Refresh_CommandBuffer *commandBuffer, - void *windowHandle, - uint32_t *pWidth, - uint32_t *pHeight + SDL_Window *window, + Uint32 *pWidth, + Uint32 *pHeight ) { - NULL_RETURN_NULL(device); - return device->AcquireSwapchainTexture( - device->driverData, + CHECK_COMMAND_BUFFER_RETURN_NULL + return COMMAND_BUFFER_DEVICE->AcquireSwapchainTexture( commandBuffer, - windowHandle, + window, pWidth, pHeight ); } -Refresh_TextureFormat Refresh_GetSwapchainFormat( - Refresh_Device *device, - void *windowHandle -) { - if (device == NULL) { return 0; } - return device->GetSwapchainFormat( - device->driverData, - windowHandle - ); -} - -void Refresh_SetSwapchainPresentMode( - Refresh_Device *device, - void *windowHandle, - Refresh_PresentMode presentMode -) { - NULL_RETURN(device); - device->SetSwapchainPresentMode( - device->driverData, - windowHandle, - presentMode - ); -} - void Refresh_Submit( - Refresh_Device *device, Refresh_CommandBuffer *commandBuffer ) { - NULL_RETURN(device); - device->Submit( - device->driverData, + CHECK_COMMAND_BUFFER + CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader*) commandBuffer; + + if ( + commandBufferHeader->renderPass.inProgress || + commandBufferHeader->computePass.inProgress || + commandBufferHeader->copyPass.inProgress + ) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Cannot submit command buffer while a pass is in progress!"); + return; + } + + commandBufferHeader->submitted = SDL_TRUE; + + COMMAND_BUFFER_DEVICE->Submit( commandBuffer ); } Refresh_Fence* Refresh_SubmitAndAcquireFence( - Refresh_Device *device, Refresh_CommandBuffer *commandBuffer ) { - NULL_RETURN_NULL(device); - return device->SubmitAndAcquireFence( - device->driverData, + CHECK_COMMAND_BUFFER_RETURN_NULL + CommandBufferCommonHeader *commandBufferHeader = (CommandBufferCommonHeader*) commandBuffer; + + if ( + commandBufferHeader->renderPass.inProgress || + commandBufferHeader->computePass.inProgress || + commandBufferHeader->copyPass.inProgress + ) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Cannot submit command buffer while a pass is in progress!"); + return NULL; + } + + commandBufferHeader->submitted = SDL_TRUE; + + return COMMAND_BUFFER_DEVICE->SubmitAndAcquireFence( commandBuffer ); } @@ -915,7 +1449,7 @@ Refresh_Fence* Refresh_SubmitAndAcquireFence( void Refresh_Wait( Refresh_Device *device ) { - NULL_RETURN(device); + NULL_ASSERT(device); device->Wait( device->driverData ); @@ -923,11 +1457,11 @@ void Refresh_Wait( void Refresh_WaitForFences( Refresh_Device *device, - uint8_t waitAll, - uint32_t fenceCount, + SDL_bool waitAll, + Uint32 fenceCount, Refresh_Fence **pFences ) { - NULL_RETURN(device); + NULL_ASSERT(device); device->WaitForFences( device->driverData, waitAll, @@ -936,13 +1470,11 @@ void Refresh_WaitForFences( ); } -int Refresh_QueryFence( +SDL_bool Refresh_QueryFence( Refresh_Device *device, Refresh_Fence *fence ) { - if (device == NULL) { - return 0; - } + if (device == NULL) { return 0; } return device->QueryFence( device->driverData, @@ -954,11 +1486,46 @@ void Refresh_ReleaseFence( Refresh_Device *device, Refresh_Fence *fence ) { - NULL_RETURN(device); + NULL_ASSERT(device); device->ReleaseFence( device->driverData, fence ); } -/* vim: set noexpandtab shiftwidth=8 tabstop=8: */ +void Refresh_OcclusionQueryBegin( + Refresh_CommandBuffer *commandBuffer, + Refresh_OcclusionQuery *query +) { + NULL_ASSERT(commandBuffer); + COMMAND_BUFFER_DEVICE->OcclusionQueryBegin( + commandBuffer, + query + ); +} + +void Refresh_OcclusionQueryEnd( + Refresh_CommandBuffer *commandBuffer, + Refresh_OcclusionQuery *query +) { + NULL_ASSERT(commandBuffer); + COMMAND_BUFFER_DEVICE->OcclusionQueryEnd( + commandBuffer, + query + ); +} + +SDL_bool Refresh_OcclusionQueryPixelCount( + Refresh_Device *device, + Refresh_OcclusionQuery *query, + Uint32 *pixelCount +) { + if (device == NULL) + return SDL_FALSE; + + return device->OcclusionQueryPixelCount( + device->driverData, + query, + pixelCount + ); +} diff --git a/src/Refresh_Driver.h b/src/Refresh_Driver.h deleted file mode 100644 index 5605d86..0000000 --- a/src/Refresh_Driver.h +++ /dev/null @@ -1,604 +0,0 @@ -/* Refresh - XNA-inspired 3D Graphics Library with modern capabilities - * - * Copyright (c) 2020 Evan Hemsley - * - * This software is provided 'as-is', without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in a - * product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source distribution. - * - * Evan "cosmonaut" Hemsley - * - */ - -#ifndef REFRESH_DRIVER_H -#define REFRESH_DRIVER_H - -#include "Refresh.h" - -/* Windows/Visual Studio cruft */ -#ifdef _WIN32 -#define inline __inline -#endif - -/* Logging */ - -#ifdef __cplusplus -extern "C" { -#endif -void Refresh_LogInfo(const char *fmt, ...); -void Refresh_LogWarn(const char *fmt, ...); -void Refresh_LogError(const char *fmt, ...); -#ifdef __cplusplus -} -#endif - -/* Internal Helper Utilities */ - -static inline uint32_t Texture_GetFormatSize( - Refresh_TextureFormat format -) { - switch (format) - { - case REFRESH_TEXTUREFORMAT_BC1: - return 8; - case REFRESH_TEXTUREFORMAT_BC2: - case REFRESH_TEXTUREFORMAT_BC3: - case REFRESH_TEXTUREFORMAT_BC7: - return 16; - case REFRESH_TEXTUREFORMAT_R8: - case REFRESH_TEXTUREFORMAT_R8_UINT: - return 1; - case REFRESH_TEXTUREFORMAT_R5G6B5: - case REFRESH_TEXTUREFORMAT_B4G4R4A4: - case REFRESH_TEXTUREFORMAT_A1R5G5B5: - case REFRESH_TEXTUREFORMAT_R16_SFLOAT: - case REFRESH_TEXTUREFORMAT_R8G8_SNORM: - case REFRESH_TEXTUREFORMAT_R8G8_UINT: - case REFRESH_TEXTUREFORMAT_R16_UINT: - return 2; - case REFRESH_TEXTUREFORMAT_R8G8B8A8: - case REFRESH_TEXTUREFORMAT_R32_SFLOAT: - case REFRESH_TEXTUREFORMAT_R16G16_SFLOAT: - case REFRESH_TEXTUREFORMAT_R8G8B8A8_SNORM: - case REFRESH_TEXTUREFORMAT_A2R10G10B10: - case REFRESH_TEXTUREFORMAT_R8G8B8A8_UINT: - case REFRESH_TEXTUREFORMAT_R16G16_UINT: - return 4; - case REFRESH_TEXTUREFORMAT_R16G16B16A16_SFLOAT: - case REFRESH_TEXTUREFORMAT_R16G16B16A16: - case REFRESH_TEXTUREFORMAT_R32G32_SFLOAT: - case REFRESH_TEXTUREFORMAT_R16G16B16A16_UINT: - return 8; - case REFRESH_TEXTUREFORMAT_R32G32B32A32_SFLOAT: - return 16; - default: - Refresh_LogError( - "Unrecognized SurfaceFormat!" - ); - return 0; - } -} - -static inline uint32_t PrimitiveVerts( - Refresh_PrimitiveType primitiveType, - uint32_t primitiveCount -) { - switch (primitiveType) - { - case REFRESH_PRIMITIVETYPE_TRIANGLELIST: - return primitiveCount * 3; - case REFRESH_PRIMITIVETYPE_TRIANGLESTRIP: - return primitiveCount + 2; - case REFRESH_PRIMITIVETYPE_LINELIST: - return primitiveCount * 2; - case REFRESH_PRIMITIVETYPE_LINESTRIP: - return primitiveCount + 1; - case REFRESH_PRIMITIVETYPE_POINTLIST: - return primitiveCount; - default: - Refresh_LogError( - "Unrecognized primitive type!" - ); - return 0; - } -} - -static inline uint32_t IndexSize(Refresh_IndexElementSize size) -{ - return (size == REFRESH_INDEXELEMENTSIZE_16BIT) ? 2 : 4; -} - -static inline uint32_t BytesPerRow( - int32_t width, - Refresh_TextureFormat format -) { - uint32_t blocksPerRow = width; - - if ( format == REFRESH_TEXTUREFORMAT_BC1 || - format == REFRESH_TEXTUREFORMAT_BC2 || - format == REFRESH_TEXTUREFORMAT_BC3 || - format == REFRESH_TEXTUREFORMAT_BC7 ) - { - blocksPerRow = (width + 3) / 4; - } - - return blocksPerRow * Texture_GetFormatSize(format); -} - -static inline int32_t BytesPerImage( - uint32_t width, - uint32_t height, - Refresh_TextureFormat format -) { - uint32_t blocksPerRow = width; - uint32_t blocksPerColumn = height; - - if ( format == REFRESH_TEXTUREFORMAT_BC1 || - format == REFRESH_TEXTUREFORMAT_BC2 || - format == REFRESH_TEXTUREFORMAT_BC3 || - format == REFRESH_TEXTUREFORMAT_BC7 ) - { - blocksPerRow = (width + 3) / 4; - blocksPerColumn = (height + 3) / 4; - } - - return blocksPerRow * blocksPerColumn * Texture_GetFormatSize(format); -} - -/* XNA GraphicsDevice Limits */ -/* TODO: can these be adjusted for modern low-end? */ - -#define MAX_TEXTURE_SAMPLERS 16 -#define MAX_VERTEXTEXTURE_SAMPLERS 4 -#define MAX_TOTAL_SAMPLERS (MAX_TEXTURE_SAMPLERS + MAX_VERTEXTEXTURE_SAMPLERS) - -#define MAX_BUFFER_BINDINGS 16 - -#define MAX_COLOR_TARGET_BINDINGS 4 -#define MAX_PRESENT_COUNT 16 - -/* Refresh_Device Definition */ - -typedef struct Refresh_Renderer Refresh_Renderer; - -struct Refresh_Device -{ - /* Quit */ - - void (*DestroyDevice)(Refresh_Device *device); - - /* Drawing */ - - void (*DrawInstancedPrimitives)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - uint32_t baseVertex, - uint32_t startIndex, - uint32_t primitiveCount, - uint32_t instanceCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset - ); - - void (*DrawIndexedPrimitives)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - uint32_t baseVertex, - uint32_t startIndex, - uint32_t primitiveCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset - ); - - void (*DrawPrimitives)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - uint32_t vertexStart, - uint32_t primitiveCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset - ); - - void (*DrawPrimitivesIndirect)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint32_t offsetInBytes, - uint32_t drawCount, - uint32_t stride, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset - ); - - void (*DispatchCompute)( - Refresh_Renderer *device, - Refresh_CommandBuffer *commandBuffer, - uint32_t groupCountX, - uint32_t groupCountY, - uint32_t groupCountZ, - uint32_t computeParamOffset - ); - - /* State Creation */ - - Refresh_ComputePipeline* (*CreateComputePipeline)( - Refresh_Renderer *driverData, - Refresh_ComputeShaderInfo *computeShaderInfo - ); - - Refresh_GraphicsPipeline* (*CreateGraphicsPipeline)( - Refresh_Renderer *driverData, - Refresh_GraphicsPipelineCreateInfo *pipelineCreateInfo - ); - - Refresh_Sampler* (*CreateSampler)( - Refresh_Renderer *driverData, - Refresh_SamplerStateCreateInfo *samplerStateCreateInfo - ); - - Refresh_ShaderModule* (*CreateShaderModule)( - Refresh_Renderer *driverData, - Refresh_ShaderModuleCreateInfo *shaderModuleCreateInfo - ); - - Refresh_Texture* (*CreateTexture)( - Refresh_Renderer *driverData, - Refresh_TextureCreateInfo *textureCreateInfo - ); - - Refresh_Buffer* (*CreateBuffer)( - Refresh_Renderer *driverData, - Refresh_BufferUsageFlags usageFlags, - uint32_t sizeInBytes - ); - - /* Setters */ - - void (*SetTextureData)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *textureSlice, - void *data, - uint32_t dataLengthInBytes - ); - - void (*SetTextureDataYUV)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer* commandBuffer, - Refresh_Texture *y, - Refresh_Texture *u, - Refresh_Texture *v, - uint32_t yWidth, - uint32_t yHeight, - uint32_t uvWidth, - uint32_t uvHeight, - void *yDataPtr, - void *uDataPtr, - void *vDataPtr, - uint32_t yDataLength, - uint32_t uvDataLength, - uint32_t yStride, - uint32_t uvStride - ); - - void (*CopyTextureToTexture)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *sourceTextureSlice, - Refresh_TextureSlice *destinationTextureSlice, - Refresh_Filter filter - ); - - void (*CopyTextureToBuffer)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *textureSlice, - Refresh_Buffer *buffer - ); - - void (*SetBufferData)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint32_t offsetInBytes, - void* data, - uint32_t dataLength - ); - - uint32_t (*PushVertexShaderUniforms)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - void *data, - uint32_t dataLengthInBytes - ); - - uint32_t (*PushFragmentShaderUniforms)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - void *data, - uint32_t dataLengthInBytes - ); - - uint32_t (*PushComputeShaderUniforms)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - void *data, - uint32_t dataLengthInBytes - ); - - void (*BindVertexSamplers)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures, - Refresh_Sampler **pSamplers - ); - - void (*BindFragmentSamplers)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures, - Refresh_Sampler **pSamplers - ); - - /* Getters */ - - void (*GetBufferData)( - Refresh_Renderer *driverData, - Refresh_Buffer *buffer, - void *data, - uint32_t dataLengthInBytes - ); - - /* Disposal */ - - void (*QueueDestroyTexture)( - Refresh_Renderer *driverData, - Refresh_Texture *texture - ); - - void (*QueueDestroySampler)( - Refresh_Renderer *driverData, - Refresh_Sampler *sampler - ); - - void (*QueueDestroyBuffer)( - Refresh_Renderer *driverData, - Refresh_Buffer *buffer - ); - - void (*QueueDestroyShaderModule)( - Refresh_Renderer *driverData, - Refresh_ShaderModule *shaderModule - ); - - void (*QueueDestroyComputePipeline)( - Refresh_Renderer *driverData, - Refresh_ComputePipeline *computePipeline - ); - - void (*QueueDestroyGraphicsPipeline)( - Refresh_Renderer *driverData, - Refresh_GraphicsPipeline *graphicsPipeline - ); - - /* Graphics State */ - - void (*BeginRenderPass)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_ColorAttachmentInfo *colorAttachmentInfos, - uint32_t colorAttachmentCount, - Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo - ); - - void (*EndRenderPass)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer - ); - - void (*SetViewport)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Viewport *viewport - ); - - void (*SetScissor)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Rect *scissor - ); - - void (*BindGraphicsPipeline)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_GraphicsPipeline *graphicsPipeline - ); - - void (*BindVertexBuffers)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - uint32_t firstBinding, - uint32_t bindingCount, - Refresh_Buffer **pBuffers, - uint64_t *pOffsets - ); - - void (*BindIndexBuffer)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint64_t offset, - Refresh_IndexElementSize indexElementSize - ); - - void (*BindComputePipeline)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_ComputePipeline *computePipeline - ); - - void (*BindComputeBuffers)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer **pBuffers - ); - - void (*BindComputeTextures)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures - ); - - uint8_t (*ClaimWindow)( - Refresh_Renderer *driverData, - void *windowHandle, - Refresh_PresentMode presentMode - ); - - void (*UnclaimWindow)( - Refresh_Renderer *driverData, - void *windowHandle - ); - - Refresh_CommandBuffer* (*AcquireCommandBuffer)( - Refresh_Renderer *driverData - ); - - Refresh_Texture* (*AcquireSwapchainTexture)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - void *windowHandle, - uint32_t *pWidth, - uint32_t *pHeight - ); - - Refresh_TextureFormat (*GetSwapchainFormat)( - Refresh_Renderer *driverData, - void *windowHandle - ); - - void (*SetSwapchainPresentMode)( - Refresh_Renderer *driverData, - void *windowHandle, - Refresh_PresentMode presentMode - ); - - void (*Submit)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer - ); - - Refresh_Fence* (*SubmitAndAcquireFence)( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer - ); - - void (*Wait)( - Refresh_Renderer *driverData - ); - - void (*WaitForFences)( - Refresh_Renderer *driverData, - uint8_t waitAll, - uint32_t fenceCount, - Refresh_Fence **pFences - ); - - int (*QueryFence)( - Refresh_Renderer *driverData, - Refresh_Fence *fence - ); - - void (*ReleaseFence)( - Refresh_Renderer *driverData, - Refresh_Fence *fence - ); - - /* Opaque pointer for the Driver */ - Refresh_Renderer *driverData; -}; - -#define ASSIGN_DRIVER_FUNC(func, name) \ - result->func = name##_##func; -#define ASSIGN_DRIVER(name) \ - ASSIGN_DRIVER_FUNC(DestroyDevice, name) \ - ASSIGN_DRIVER_FUNC(DrawIndexedPrimitives, name) \ - ASSIGN_DRIVER_FUNC(DrawInstancedPrimitives, name) \ - ASSIGN_DRIVER_FUNC(DrawPrimitives, name) \ - ASSIGN_DRIVER_FUNC(DrawPrimitivesIndirect, name) \ - ASSIGN_DRIVER_FUNC(DispatchCompute, name) \ - ASSIGN_DRIVER_FUNC(CreateComputePipeline, name) \ - ASSIGN_DRIVER_FUNC(CreateGraphicsPipeline, name) \ - ASSIGN_DRIVER_FUNC(CreateSampler, name) \ - ASSIGN_DRIVER_FUNC(CreateShaderModule, name) \ - ASSIGN_DRIVER_FUNC(CreateTexture, name) \ - ASSIGN_DRIVER_FUNC(CreateBuffer, name) \ - ASSIGN_DRIVER_FUNC(SetTextureData, name) \ - ASSIGN_DRIVER_FUNC(SetTextureDataYUV, name) \ - ASSIGN_DRIVER_FUNC(CopyTextureToTexture, name) \ - ASSIGN_DRIVER_FUNC(CopyTextureToBuffer, name) \ - ASSIGN_DRIVER_FUNC(SetBufferData, name) \ - ASSIGN_DRIVER_FUNC(PushVertexShaderUniforms, name) \ - ASSIGN_DRIVER_FUNC(PushFragmentShaderUniforms, name) \ - ASSIGN_DRIVER_FUNC(PushComputeShaderUniforms, name) \ - ASSIGN_DRIVER_FUNC(BindVertexSamplers, name) \ - ASSIGN_DRIVER_FUNC(BindFragmentSamplers, name) \ - ASSIGN_DRIVER_FUNC(GetBufferData, name) \ - ASSIGN_DRIVER_FUNC(QueueDestroyTexture, name) \ - ASSIGN_DRIVER_FUNC(QueueDestroySampler, name) \ - ASSIGN_DRIVER_FUNC(QueueDestroyBuffer, name) \ - ASSIGN_DRIVER_FUNC(QueueDestroyShaderModule, name) \ - ASSIGN_DRIVER_FUNC(QueueDestroyComputePipeline, name) \ - ASSIGN_DRIVER_FUNC(QueueDestroyGraphicsPipeline, name) \ - ASSIGN_DRIVER_FUNC(BeginRenderPass, name) \ - ASSIGN_DRIVER_FUNC(EndRenderPass, name) \ - ASSIGN_DRIVER_FUNC(SetViewport, name) \ - ASSIGN_DRIVER_FUNC(SetScissor, name) \ - ASSIGN_DRIVER_FUNC(BindGraphicsPipeline, name) \ - ASSIGN_DRIVER_FUNC(BindVertexBuffers, name) \ - ASSIGN_DRIVER_FUNC(BindIndexBuffer, name) \ - ASSIGN_DRIVER_FUNC(BindComputePipeline, name) \ - ASSIGN_DRIVER_FUNC(BindComputeBuffers, name) \ - ASSIGN_DRIVER_FUNC(BindComputeTextures, name) \ - ASSIGN_DRIVER_FUNC(ClaimWindow, name) \ - ASSIGN_DRIVER_FUNC(UnclaimWindow, name) \ - ASSIGN_DRIVER_FUNC(AcquireCommandBuffer, name) \ - ASSIGN_DRIVER_FUNC(AcquireSwapchainTexture, name) \ - ASSIGN_DRIVER_FUNC(GetSwapchainFormat, name) \ - ASSIGN_DRIVER_FUNC(SetSwapchainPresentMode, name) \ - ASSIGN_DRIVER_FUNC(Submit, name) \ - ASSIGN_DRIVER_FUNC(SubmitAndAcquireFence, name) \ - ASSIGN_DRIVER_FUNC(Wait, name) \ - ASSIGN_DRIVER_FUNC(WaitForFences, name) \ - ASSIGN_DRIVER_FUNC(QueryFence, name) \ - ASSIGN_DRIVER_FUNC(ReleaseFence, name) - -typedef struct Refresh_Driver -{ - const char *Name; - uint8_t (*PrepareDriver)(uint32_t *flags); - Refresh_Device* (*CreateDevice)( - uint8_t debugMode - ); -} Refresh_Driver; - -extern Refresh_Driver VulkanDriver; -extern Refresh_Driver PS5Driver; - -#endif /* REFRESH_DRIVER_H */ - -/* vim: set noexpandtab shiftwidth=8 tabstop=8: */ diff --git a/src/Refresh_Driver_Vulkan.c b/src/Refresh_Driver_Vulkan.c deleted file mode 100644 index 72f0c10..0000000 --- a/src/Refresh_Driver_Vulkan.c +++ /dev/null @@ -1,12260 +0,0 @@ -/* Refresh - XNA-inspired 3D Graphics Library with modern capabilities - * - * Copyright (c) 2020 Evan Hemsley - * - * This software is provided 'as-is', without any express or implied warranty. - * In no event will the authors be held liable for any damages arising from - * the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software in a - * product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source distribution. - * - * Evan "cosmonaut" Hemsley - * - */ - -#if REFRESH_DRIVER_VULKAN - -/* Needed for VK_KHR_portability_subset */ -#define VK_ENABLE_BETA_EXTENSIONS - -#define VK_NO_PROTOTYPES -#include "vulkan/vulkan.h" - -#include "Refresh_Driver.h" - -#include -#include -#include - -#define VULKAN_INTERNAL_clamp(val, min, max) SDL_max(min, SDL_min(val, max)) - -/* Global Vulkan Loader Entry Points */ - -static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; - -#define VULKAN_GLOBAL_FUNCTION(name) \ - static PFN_##name name = NULL; -#include "Refresh_Driver_Vulkan_vkfuncs.h" - -/* vkInstance/vkDevice function typedefs */ - -#define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \ - typedef ret (VKAPI_CALL *vkfntype_##func) params; -#define VULKAN_DEVICE_FUNCTION(ext, ret, func, params) \ - typedef ret (VKAPI_CALL *vkfntype_##func) params; -#include "Refresh_Driver_Vulkan_vkfuncs.h" - -typedef struct VulkanExtensions -{ - /* Globally supported */ - uint8_t KHR_swapchain; - /* Core since 1.1 */ - uint8_t KHR_maintenance1; - uint8_t KHR_get_memory_requirements2; - - /* Core since 1.2 */ - uint8_t KHR_driver_properties; - /* EXT, probably not going to be Core */ - uint8_t EXT_vertex_attribute_divisor; - /* Only required for special implementations (i.e. MoltenVK) */ - uint8_t KHR_portability_subset; -} VulkanExtensions; - -/* Defines */ - -#define STARTING_ALLOCATION_SIZE 64000000 /* 64MB */ -#define MAX_ALLOCATION_SIZE 256000000 /* 256MB */ -#define ALLOCATION_INCREMENT 16000000 /* 16MB */ -#define TRANSFER_BUFFER_STARTING_SIZE 8000000 /* 8MB */ -#define POOLED_TRANSFER_BUFFER_SIZE 16000000 /* 16MB */ -#define UBO_BUFFER_SIZE 16777216 /* 16MB */ -#define UBO_SECTION_SIZE 4096 /* 4KB */ -#define DESCRIPTOR_POOL_STARTING_SIZE 128 -#define DEFRAG_TIME 200 -#define WINDOW_DATA "Refresh_VulkanWindowData" - -#define IDENTITY_SWIZZLE \ -{ \ - VK_COMPONENT_SWIZZLE_IDENTITY, \ - VK_COMPONENT_SWIZZLE_IDENTITY, \ - VK_COMPONENT_SWIZZLE_IDENTITY, \ - VK_COMPONENT_SWIZZLE_IDENTITY \ -} - -#define NULL_DESC_LAYOUT (VkDescriptorSetLayout) 0 -#define NULL_PIPELINE_LAYOUT (VkPipelineLayout) 0 -#define NULL_RENDER_PASS (Refresh_RenderPass*) 0 - -#define EXPAND_ELEMENTS_IF_NEEDED(arr, initialValue, type) \ - if (arr->count == arr->capacity) \ - { \ - if (arr->capacity == 0) \ - { \ - arr->capacity = initialValue; \ - } \ - else \ - { \ - arr->capacity *= 2; \ - } \ - arr->elements = (type*) SDL_realloc( \ - arr->elements, \ - arr->capacity * sizeof(type) \ - ); \ - } - -#define EXPAND_ARRAY_IF_NEEDED(arr, elementType, newCount, capacity, newCapacity) \ - if (newCount >= capacity) \ - { \ - capacity = newCapacity; \ - arr = (elementType*) SDL_realloc( \ - arr, \ - sizeof(elementType) * capacity \ - ); \ - } - -#define MOVE_ARRAY_CONTENTS_AND_RESET(i, dstArr, dstCount, srcArr, srcCount) \ - for (i = 0; i < srcCount; i += 1) \ - { \ - dstArr[i] = srcArr[i]; \ - } \ - dstCount = srcCount; \ - srcCount = 0; - -/* Enums */ - -typedef enum VulkanResourceAccessType -{ - /* Reads */ - RESOURCE_ACCESS_NONE, /* For initialization */ - RESOURCE_ACCESS_INDEX_BUFFER, - RESOURCE_ACCESS_VERTEX_BUFFER, - RESOURCE_ACCESS_INDIRECT_BUFFER, - RESOURCE_ACCESS_VERTEX_SHADER_READ_UNIFORM_BUFFER, - RESOURCE_ACCESS_VERTEX_SHADER_READ_SAMPLED_IMAGE, - RESOURCE_ACCESS_FRAGMENT_SHADER_READ_UNIFORM_BUFFER, - RESOURCE_ACCESS_FRAGMENT_SHADER_READ_SAMPLED_IMAGE, - RESOURCE_ACCESS_FRAGMENT_SHADER_READ_COLOR_ATTACHMENT, - RESOURCE_ACCESS_FRAGMENT_SHADER_READ_DEPTH_STENCIL_ATTACHMENT, - RESOURCE_ACCESS_COMPUTE_SHADER_READ_UNIFORM_BUFFER, - RESOURCE_ACCESS_COMPUTE_SHADER_READ_SAMPLED_IMAGE_OR_UNIFORM_TEXEL_BUFFER, - RESOURCE_ACCESS_COMPUTE_SHADER_READ_OTHER, - RESOURCE_ACCESS_ANY_SHADER_READ_SAMPLED_IMAGE, - RESOURCE_ACCESS_COLOR_ATTACHMENT_READ, - RESOURCE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ, - RESOURCE_ACCESS_TRANSFER_READ, - RESOURCE_ACCESS_HOST_READ, - RESOURCE_ACCESS_PRESENT, - RESOURCE_ACCESS_END_OF_READ, - - /* Writes */ - RESOURCE_ACCESS_VERTEX_SHADER_WRITE, - RESOURCE_ACCESS_FRAGMENT_SHADER_WRITE, - RESOURCE_ACCESS_COLOR_ATTACHMENT_WRITE, - RESOURCE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE, - RESOURCE_ACCESS_TRANSFER_WRITE, - RESOURCE_ACCESS_HOST_WRITE, - - /* Read-Writes */ - RESOURCE_ACCESS_COLOR_ATTACHMENT_READ_WRITE, - RESOURCE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_WRITE, - RESOURCE_ACCESS_COMPUTE_SHADER_STORAGE_IMAGE_READ_WRITE, - RESOURCE_ACCESS_COMPUTE_SHADER_BUFFER_READ_WRITE, - RESOURCE_ACCESS_TRANSFER_READ_WRITE, - RESOURCE_ACCESS_GENERAL, - - /* Count */ - RESOURCE_ACCESS_TYPES_COUNT -} VulkanResourceAccessType; - -/* Conversions */ - -static const uint8_t DEVICE_PRIORITY[] = -{ - 0, /* VK_PHYSICAL_DEVICE_TYPE_OTHER */ - 3, /* VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU */ - 4, /* VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU */ - 2, /* VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU */ - 1 /* VK_PHYSICAL_DEVICE_TYPE_CPU */ -}; - -static VkFormat RefreshToVK_SurfaceFormat[] = -{ - VK_FORMAT_R8G8B8A8_UNORM, /* R8G8B8A8_UNORM */ - VK_FORMAT_B8G8R8A8_UNORM, /* B8G8R8A8_UNORM */ - VK_FORMAT_R5G6B5_UNORM_PACK16, /* R5G6B5_UNORM */ - VK_FORMAT_A1R5G5B5_UNORM_PACK16, /* A1R5G5B5_UNORM */ - VK_FORMAT_B4G4R4A4_UNORM_PACK16, /* B4G4R4A4_UNORM */ - VK_FORMAT_A2R10G10B10_UNORM_PACK32, /* A2R10G10B10_UNORM */ - VK_FORMAT_R16G16_UNORM, /* R16G16_UNORM */ - VK_FORMAT_R16G16B16A16_UNORM, /* R16G16B16A16_UNORM */ - VK_FORMAT_R8_UNORM, /* R8_UNORM */ - VK_FORMAT_BC1_RGBA_UNORM_BLOCK, /* BC1_UNORM */ - VK_FORMAT_BC2_UNORM_BLOCK, /* BC2_UNORM */ - VK_FORMAT_BC3_UNORM_BLOCK, /* BC3_UNORM */ - VK_FORMAT_BC7_UNORM_BLOCK, /* BC7_UNORM */ - VK_FORMAT_R8G8_SNORM, /* R8G8_SNORM */ - VK_FORMAT_R8G8B8A8_SNORM, /* R8G8B8A8_SNORM */ - VK_FORMAT_R16_SFLOAT, /* R16_SFLOAT */ - VK_FORMAT_R16G16_SFLOAT, /* R16G16_SFLOAT */ - VK_FORMAT_R16G16B16A16_SFLOAT, /* R16G16B16A16_SFLOAT */ - VK_FORMAT_R32_SFLOAT, /* R32_SFLOAT */ - VK_FORMAT_R32G32_SFLOAT, /* R32G32_SFLOAT */ - VK_FORMAT_R32G32B32A32_SFLOAT, /* R32G32B32A32_SFLOAT */ - VK_FORMAT_R8_UINT, /* R8_UINT */ - VK_FORMAT_R8G8_UINT, /* R8G8_UINT */ - VK_FORMAT_R8G8B8A8_UINT, /* R8G8B8A8_UINT */ - VK_FORMAT_R16_UINT, /* R16_UINT */ - VK_FORMAT_R16G16_UINT, /* R16G16_UINT */ - VK_FORMAT_R16G16B16A16_UINT, /* R16G16B16A16_UINT */ - VK_FORMAT_D16_UNORM, /* D16_UNORM */ - VK_FORMAT_D32_SFLOAT, /* D32_SFLOAT */ - VK_FORMAT_D16_UNORM_S8_UINT, /* D16_UNORM_S8_UINT */ - VK_FORMAT_D32_SFLOAT_S8_UINT /* D32_SFLOAT_S8_UINT */ -}; - -static VkFormat RefreshToVK_VertexFormat[] = -{ - VK_FORMAT_R32_UINT, /* UINT */ - VK_FORMAT_R32_SFLOAT, /* FLOAT */ - VK_FORMAT_R32G32_SFLOAT, /* VECTOR2 */ - VK_FORMAT_R32G32B32_SFLOAT, /* VECTOR3 */ - VK_FORMAT_R32G32B32A32_SFLOAT, /* VECTOR4 */ - VK_FORMAT_R8G8B8A8_UNORM, /* COLOR */ - VK_FORMAT_R8G8B8A8_USCALED, /* BYTE4 */ - VK_FORMAT_R16G16_SSCALED, /* SHORT2 */ - VK_FORMAT_R16G16B16A16_SSCALED, /* SHORT4 */ - VK_FORMAT_R16G16_SNORM, /* NORMALIZEDSHORT2 */ - VK_FORMAT_R16G16B16A16_SNORM, /* NORMALIZEDSHORT4 */ - VK_FORMAT_R16G16_SFLOAT, /* HALFVECTOR2 */ - VK_FORMAT_R16G16B16A16_SFLOAT /* HALFVECTOR4 */ -}; - -static VkIndexType RefreshToVK_IndexType[] = -{ - VK_INDEX_TYPE_UINT16, - VK_INDEX_TYPE_UINT32 -}; - -static VkPrimitiveTopology RefreshToVK_PrimitiveType[] = -{ - VK_PRIMITIVE_TOPOLOGY_POINT_LIST, - VK_PRIMITIVE_TOPOLOGY_LINE_LIST, - VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP -}; - -static VkPolygonMode RefreshToVK_PolygonMode[] = -{ - VK_POLYGON_MODE_FILL, - VK_POLYGON_MODE_LINE, - VK_POLYGON_MODE_POINT -}; - -static VkCullModeFlags RefreshToVK_CullMode[] = -{ - VK_CULL_MODE_NONE, - VK_CULL_MODE_FRONT_BIT, - VK_CULL_MODE_BACK_BIT, - VK_CULL_MODE_FRONT_AND_BACK -}; - -static VkFrontFace RefreshToVK_FrontFace[] = -{ - VK_FRONT_FACE_COUNTER_CLOCKWISE, - VK_FRONT_FACE_CLOCKWISE -}; - -static VkBlendFactor RefreshToVK_BlendFactor[] = -{ - VK_BLEND_FACTOR_ZERO, - VK_BLEND_FACTOR_ONE, - VK_BLEND_FACTOR_SRC_COLOR, - VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR, - VK_BLEND_FACTOR_DST_COLOR, - VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, - VK_BLEND_FACTOR_SRC_ALPHA, - VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, - VK_BLEND_FACTOR_DST_ALPHA, - VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, - VK_BLEND_FACTOR_CONSTANT_COLOR, - VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, - VK_BLEND_FACTOR_CONSTANT_ALPHA, - VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, - VK_BLEND_FACTOR_SRC_ALPHA_SATURATE -}; - -static VkBlendOp RefreshToVK_BlendOp[] = -{ - VK_BLEND_OP_ADD, - VK_BLEND_OP_SUBTRACT, - VK_BLEND_OP_REVERSE_SUBTRACT, - VK_BLEND_OP_MIN, - VK_BLEND_OP_MAX -}; - -static VkCompareOp RefreshToVK_CompareOp[] = -{ - VK_COMPARE_OP_NEVER, - VK_COMPARE_OP_LESS, - VK_COMPARE_OP_EQUAL, - VK_COMPARE_OP_LESS_OR_EQUAL, - VK_COMPARE_OP_GREATER, - VK_COMPARE_OP_NOT_EQUAL, - VK_COMPARE_OP_GREATER_OR_EQUAL, - VK_COMPARE_OP_ALWAYS -}; - -static VkStencilOp RefreshToVK_StencilOp[] = -{ - VK_STENCIL_OP_KEEP, - VK_STENCIL_OP_ZERO, - VK_STENCIL_OP_REPLACE, - VK_STENCIL_OP_INCREMENT_AND_CLAMP, - VK_STENCIL_OP_DECREMENT_AND_CLAMP, - VK_STENCIL_OP_INVERT, - VK_STENCIL_OP_INCREMENT_AND_WRAP, - VK_STENCIL_OP_DECREMENT_AND_WRAP -}; - -static VkAttachmentLoadOp RefreshToVK_LoadOp[] = -{ - VK_ATTACHMENT_LOAD_OP_LOAD, - VK_ATTACHMENT_LOAD_OP_CLEAR, - VK_ATTACHMENT_LOAD_OP_DONT_CARE -}; - -static VkAttachmentStoreOp RefreshToVK_StoreOp[] = -{ - VK_ATTACHMENT_STORE_OP_STORE, - VK_ATTACHMENT_STORE_OP_DONT_CARE -}; - -static VkSampleCountFlagBits RefreshToVK_SampleCount[] = -{ - VK_SAMPLE_COUNT_1_BIT, - VK_SAMPLE_COUNT_2_BIT, - VK_SAMPLE_COUNT_4_BIT, - VK_SAMPLE_COUNT_8_BIT, - VK_SAMPLE_COUNT_16_BIT, - VK_SAMPLE_COUNT_32_BIT, - VK_SAMPLE_COUNT_64_BIT -}; - -static VkVertexInputRate RefreshToVK_VertexInputRate[] = -{ - VK_VERTEX_INPUT_RATE_VERTEX, - VK_VERTEX_INPUT_RATE_INSTANCE -}; - -static VkFilter RefreshToVK_Filter[] = -{ - VK_FILTER_NEAREST, - VK_FILTER_LINEAR, - VK_FILTER_CUBIC_EXT -}; - -static VkSamplerMipmapMode RefreshToVK_SamplerMipmapMode[] = -{ - VK_SAMPLER_MIPMAP_MODE_NEAREST, - VK_SAMPLER_MIPMAP_MODE_LINEAR -}; - -static VkSamplerAddressMode RefreshToVK_SamplerAddressMode[] = -{ - 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 -}; - -static VkBorderColor RefreshToVK_BorderColor[] = -{ - VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, - VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, - VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, - VK_BORDER_COLOR_INT_OPAQUE_BLACK, - VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, - VK_BORDER_COLOR_INT_OPAQUE_WHITE -}; - -/* Structures */ - -/* Memory Allocation */ - -typedef struct VulkanMemoryAllocation VulkanMemoryAllocation; -typedef struct VulkanBuffer VulkanBuffer; -typedef struct VulkanTexture VulkanTexture; - -typedef struct VulkanMemoryFreeRegion -{ - VulkanMemoryAllocation *allocation; - VkDeviceSize offset; - VkDeviceSize size; - uint32_t allocationIndex; - uint32_t sortedIndex; -} VulkanMemoryFreeRegion; - -typedef struct VulkanMemoryUsedRegion -{ - VulkanMemoryAllocation *allocation; - VkDeviceSize offset; - VkDeviceSize size; - VkDeviceSize resourceOffset; /* differs from offset based on alignment*/ - VkDeviceSize resourceSize; /* differs from size based on alignment */ - VkDeviceSize alignment; - uint8_t isBuffer; - REFRESHNAMELESS union - { - VulkanBuffer *vulkanBuffer; - VulkanTexture *vulkanTexture; - }; -} VulkanMemoryUsedRegion; - -typedef struct VulkanMemorySubAllocator -{ - uint32_t memoryTypeIndex; - VkDeviceSize nextAllocationSize; - VulkanMemoryAllocation **allocations; - uint32_t allocationCount; - VulkanMemoryFreeRegion **sortedFreeRegions; - uint32_t sortedFreeRegionCount; - uint32_t sortedFreeRegionCapacity; -} VulkanMemorySubAllocator; - -struct VulkanMemoryAllocation -{ - VulkanMemorySubAllocator *allocator; - VkDeviceMemory memory; - VkDeviceSize size; - VulkanMemoryUsedRegion **usedRegions; - uint32_t usedRegionCount; - uint32_t usedRegionCapacity; - VulkanMemoryFreeRegion **freeRegions; - uint32_t freeRegionCount; - uint32_t freeRegionCapacity; - uint8_t dedicated; - uint8_t availableForAllocation; - VkDeviceSize freeSpace; - VkDeviceSize usedSpace; - uint8_t *mapPointer; - SDL_mutex *memoryLock; -}; - -typedef struct VulkanMemoryAllocator -{ - VulkanMemorySubAllocator subAllocators[VK_MAX_MEMORY_TYPES]; -} VulkanMemoryAllocator; - -/* Memory Barriers */ - -typedef struct VulkanResourceAccessInfo -{ - VkPipelineStageFlags stageMask; - VkAccessFlags accessMask; - VkImageLayout imageLayout; -} VulkanResourceAccessInfo; - -static const VulkanResourceAccessInfo AccessMap[RESOURCE_ACCESS_TYPES_COUNT] = -{ - /* RESOURCE_ACCESS_NONE */ - { - 0, - 0, - VK_IMAGE_LAYOUT_UNDEFINED - }, - - /* RESOURCE_ACCESS_INDEX_BUFFER */ - { - VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, - VK_ACCESS_INDEX_READ_BIT, - VK_IMAGE_LAYOUT_UNDEFINED - }, - - /* RESOURCE_ACCESS_VERTEX_BUFFER */ - { - VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, - VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, - VK_IMAGE_LAYOUT_UNDEFINED - }, - - /* RESOURCE_ACCESS_INDIRECT_BUFFER */ - { - VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, - VK_ACCESS_INDIRECT_COMMAND_READ_BIT, - VK_IMAGE_LAYOUT_UNDEFINED - }, - - /* RESOURCE_ACCESS_VERTEX_SHADER_READ_UNIFORM_BUFFER */ - { - VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_IMAGE_LAYOUT_UNDEFINED - }, - - /* RESOURCE_ACCESS_VERTEX_SHADER_READ_SAMPLED_IMAGE */ - { - VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - }, - - /* RESOURCE_ACCESS_FRAGMENT_SHADER_READ_UNIFORM_BUFFER */ - { - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_ACCESS_UNIFORM_READ_BIT, - VK_IMAGE_LAYOUT_UNDEFINED - }, - - /* RESOURCE_ACCESS_FRAGMENT_SHADER_READ_SAMPLED_IMAGE */ - { - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - }, - - /* RESOURCE_ACCESS_FRAGMENT_SHADER_READ_COLOR_ATTACHMENT */ - { - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_ACCESS_INPUT_ATTACHMENT_READ_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - }, - - /* RESOURCE_ACCESS_FRAGMENT_SHADER_READ_DEPTH_STENCIL_ATTACHMENT */ - { - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_ACCESS_INPUT_ATTACHMENT_READ_BIT, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL - }, - - /* RESOURCE_ACCESS_COMPUTE_SHADER_READ_UNIFORM_BUFFER */ - { - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_ACCESS_UNIFORM_READ_BIT, - VK_IMAGE_LAYOUT_UNDEFINED - }, - - /* RESOURCE_ACCESS_COMPUTE_SHADER_READ_SAMPLED_IMAGE_OR_UNIFORM_TEXEL_BUFFER */ - { VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - }, - - /* RESOURCE_ACCESS_COMPUTE_SHADER_READ_OTHER */ - { - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_IMAGE_LAYOUT_UNDEFINED - }, - - /* RESOURCE_ACCESS_ANY_SHADER_READ_SAMPLED_IMAGE */ - { - VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL - }, - - /* RESOURCE_ACCESS_COLOR_ATTACHMENT_READ */ - { - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL - }, - - /* RESOURCE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ */ - { - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL - }, - - /* RESOURCE_ACCESS_TRANSFER_READ */ - { - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_READ_BIT, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL - }, - - /* RESOURCE_ACCESS_HOST_READ */ - { - VK_PIPELINE_STAGE_HOST_BIT, - VK_ACCESS_HOST_READ_BIT, - VK_IMAGE_LAYOUT_GENERAL - }, - - /* RESOURCE_ACCESS_PRESENT */ - { - 0, - 0, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR - }, - - /* RESOURCE_ACCESS_END_OF_READ */ - { - 0, - 0, - VK_IMAGE_LAYOUT_UNDEFINED - }, - - /* RESOURCE_ACCESS_VERTEX_SHADER_WRITE */ - { - VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, - VK_ACCESS_SHADER_WRITE_BIT, - VK_IMAGE_LAYOUT_GENERAL - }, - - /* RESOURCE_ACCESS_FRAGMENT_SHADER_WRITE */ - { - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_ACCESS_SHADER_WRITE_BIT, - VK_IMAGE_LAYOUT_GENERAL - }, - - /* RESOURCE_ACCESS_COLOR_ATTACHMENT_WRITE */ - { - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL - }, - - /* RESOURCE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE */ - { - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL - }, - - /* RESOURCE_ACCESS_TRANSFER_WRITE */ - { - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL - }, - - /* RESOURCE_ACCESS_HOST_WRITE */ - { - VK_PIPELINE_STAGE_HOST_BIT, - VK_ACCESS_HOST_WRITE_BIT, - VK_IMAGE_LAYOUT_GENERAL - }, - - /* RESOURCE_ACCESS_COLOR_ATTACHMENT_READ_WRITE */ - { - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL - }, - - /* RESOURCE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_WRITE */ - { - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL - }, - - /* RESOURCE_ACCESS_COMPUTE_SHADER_STORAGE_IMAGE_READ_WRITE */ - { - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, - VK_IMAGE_LAYOUT_GENERAL - }, - - /* RESOURCE_ACCESS_COMPUTE_SHADER_BUFFER_READ_WRITE */ - { - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, - VK_IMAGE_LAYOUT_UNDEFINED - }, - - /* RESOURCE_ACCESS_TRANSFER_READ_WRITE */ - { - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, - VK_IMAGE_LAYOUT_UNDEFINED - }, - - /* RESOURCE_ACCESS_GENERAL */ - { - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, - VK_IMAGE_LAYOUT_GENERAL - } -}; - -/* Memory structures */ - -typedef struct VulkanBufferContainer /* cast from Refresh_Buffer */ -{ - VulkanBuffer *vulkanBuffer; -} VulkanBufferContainer; - -struct VulkanBuffer -{ - VkBuffer buffer; - VkDeviceSize size; - VulkanMemoryUsedRegion *usedRegion; - VulkanResourceAccessType resourceAccessType; - VkBufferUsageFlags usage; - - uint8_t requireHostVisible; - uint8_t preferDeviceLocal; - - SDL_atomic_t referenceCount; /* Tracks command buffer usage */ - - VulkanBufferContainer *container; -}; - -typedef struct VulkanUniformBufferPool VulkanUniformBufferPool; - -typedef struct VulkanUniformBuffer -{ - VulkanUniformBufferPool *pool; - VkDeviceSize poolOffset; /* memory offset relative to the pool buffer */ - VkDeviceSize offset; /* based on uniform pushes */ - VkDescriptorSet descriptorSet; -} VulkanUniformBuffer; - -typedef enum VulkanUniformBufferType -{ - UNIFORM_BUFFER_VERTEX, - UNIFORM_BUFFER_FRAGMENT, - UNIFORM_BUFFER_COMPUTE -} VulkanUniformBufferType; - -/* Yes, the pool is made of multiple pools. - * For some reason it was considered a good idea to make VkDescriptorPool fixed-size. - */ -typedef struct VulkanUniformDescriptorPool -{ - VkDescriptorPool* descriptorPools; - uint32_t descriptorPoolCount; - - /* Decremented whenever a descriptor set is allocated and - * incremented whenever a descriptor pool is allocated. - * This lets us keep track of when we need a new pool. - */ - uint32_t availableDescriptorSetCount; -} VulkanUniformDescriptorPool; - -/* This is actually just one buffer that we carve slices out of. */ -struct VulkanUniformBufferPool -{ - VulkanUniformBufferType type; - VulkanUniformDescriptorPool descriptorPool; - VulkanBuffer *buffer; - VkDeviceSize nextAvailableOffset; - SDL_mutex *lock; - - VulkanUniformBuffer **availableBuffers; - uint32_t availableBufferCount; - uint32_t availableBufferCapacity; -}; - -/* Renderer Structure */ - -typedef struct QueueFamilyIndices -{ - uint32_t graphicsFamily; - uint32_t presentFamily; - uint32_t computeFamily; - uint32_t transferFamily; -} QueueFamilyIndices; - -typedef struct VulkanSampler -{ - VkSampler sampler; - SDL_atomic_t referenceCount; -} VulkanSampler; - -typedef struct VulkanShaderModule -{ - VkShaderModule shaderModule; - SDL_atomic_t referenceCount; -} VulkanShaderModule; - -typedef struct VulkanTextureContainer /* Cast from Refresh_Texture */ -{ - VulkanTexture *vulkanTexture; -} VulkanTextureContainer; - -struct VulkanTexture -{ - VulkanMemoryUsedRegion *usedRegion; - - VkImage image; - VkImageView view; - VkExtent2D dimensions; - - uint8_t is3D; - uint8_t isCube; - - uint32_t depth; - uint32_t layerCount; - uint32_t levelCount; - Refresh_SampleCount sampleCount; - VkFormat format; - VulkanResourceAccessType resourceAccessType; - VkImageUsageFlags usageFlags; - - VkImageAspectFlags aspectFlags; - - struct VulkanTexture *msaaTex; - - SDL_atomic_t referenceCount; - - VulkanTextureContainer *container; -}; - -typedef struct VulkanRenderTarget -{ - VkImageView view; -} VulkanRenderTarget; - -typedef struct VulkanFramebuffer -{ - VkFramebuffer framebuffer; - SDL_atomic_t referenceCount; -} VulkanFramebuffer; - -typedef struct VulkanSwapchainData -{ - /* Window surface */ - VkSurfaceKHR surface; - VkSurfaceFormatKHR surfaceFormat; - - /* Swapchain for window surface */ - VkSwapchainKHR swapchain; - VkFormat swapchainFormat; - VkComponentMapping swapchainSwizzle; - VkPresentModeKHR presentMode; - - /* Swapchain images */ - VkExtent2D extent; - VulkanTextureContainer *textureContainers; - uint32_t imageCount; - - /* Synchronization primitives */ - VkSemaphore imageAvailableSemaphore; - VkSemaphore renderFinishedSemaphore; -} VulkanSwapchainData; - -typedef struct WindowData -{ - void *windowHandle; - VkPresentModeKHR preferredPresentMode; - VulkanSwapchainData *swapchainData; -} WindowData; - -typedef struct SwapChainSupportDetails -{ - VkSurfaceCapabilitiesKHR capabilities; - VkSurfaceFormatKHR *formats; - uint32_t formatsLength; - VkPresentModeKHR *presentModes; - uint32_t presentModesLength; -} SwapChainSupportDetails; - -typedef struct VulkanPresentData -{ - WindowData *windowData; - uint32_t swapchainImageIndex; -} VulkanPresentData; - -typedef struct DescriptorSetCache DescriptorSetCache; - -typedef struct VulkanGraphicsPipelineLayout -{ - VkPipelineLayout pipelineLayout; - DescriptorSetCache *vertexSamplerDescriptorSetCache; - DescriptorSetCache *fragmentSamplerDescriptorSetCache; -} VulkanGraphicsPipelineLayout; - -typedef struct VulkanGraphicsPipeline -{ - VkPipeline pipeline; - VulkanGraphicsPipelineLayout *pipelineLayout; - Refresh_PrimitiveType primitiveType; - VkDeviceSize vertexUniformBlockSize; - VkDeviceSize fragmentUniformBlockSize; - - VulkanShaderModule *vertexShaderModule; - VulkanShaderModule *fragmentShaderModule; - - SDL_atomic_t referenceCount; -} VulkanGraphicsPipeline; - -typedef struct VulkanComputePipelineLayout -{ - VkPipelineLayout pipelineLayout; - DescriptorSetCache *bufferDescriptorSetCache; - DescriptorSetCache *imageDescriptorSetCache; -} VulkanComputePipelineLayout; - -typedef struct VulkanComputePipeline -{ - VkPipeline pipeline; - VulkanComputePipelineLayout *pipelineLayout; - VkDeviceSize uniformBlockSize; /* permanently set in Create function */ - - VulkanShaderModule *computeShaderModule; - SDL_atomic_t referenceCount; -} VulkanComputePipeline; - -/* Cache structures */ - -/* Descriptor Set Layout Caches*/ - -#define NUM_DESCRIPTOR_SET_LAYOUT_BUCKETS 1031 - -typedef struct DescriptorSetLayoutHash -{ - VkDescriptorType descriptorType; - uint32_t bindingCount; - VkShaderStageFlagBits stageFlag; -} DescriptorSetLayoutHash; - -typedef struct DescriptorSetLayoutHashMap -{ - DescriptorSetLayoutHash key; - VkDescriptorSetLayout value; -} DescriptorSetLayoutHashMap; - -typedef struct DescriptorSetLayoutHashArray -{ - DescriptorSetLayoutHashMap *elements; - int32_t count; - int32_t capacity; -} DescriptorSetLayoutHashArray; - -typedef struct DescriptorSetLayoutHashTable -{ - DescriptorSetLayoutHashArray buckets[NUM_DESCRIPTOR_SET_LAYOUT_BUCKETS]; -} DescriptorSetLayoutHashTable; - -static inline uint64_t DescriptorSetLayoutHashTable_GetHashCode(DescriptorSetLayoutHash key) -{ - const uint64_t HASH_FACTOR = 97; - uint64_t result = 1; - result = result * HASH_FACTOR + key.descriptorType; - result = result * HASH_FACTOR + key.bindingCount; - result = result * HASH_FACTOR + key.stageFlag; - return result; -} - -static inline VkDescriptorSetLayout DescriptorSetLayoutHashTable_Fetch( - DescriptorSetLayoutHashTable *table, - DescriptorSetLayoutHash key -) { - int32_t i; - uint64_t hashcode = DescriptorSetLayoutHashTable_GetHashCode(key); - DescriptorSetLayoutHashArray *arr = &table->buckets[hashcode % NUM_DESCRIPTOR_SET_LAYOUT_BUCKETS]; - - for (i = 0; i < arr->count; i += 1) - { - const DescriptorSetLayoutHash *e = &arr->elements[i].key; - if ( key.descriptorType == e->descriptorType && - key.bindingCount == e->bindingCount && - key.stageFlag == e->stageFlag ) - { - return arr->elements[i].value; - } - } - - return VK_NULL_HANDLE; -} - -static inline void DescriptorSetLayoutHashTable_Insert( - DescriptorSetLayoutHashTable *table, - DescriptorSetLayoutHash key, - VkDescriptorSetLayout value -) { - uint64_t hashcode = DescriptorSetLayoutHashTable_GetHashCode(key); - DescriptorSetLayoutHashArray *arr = &table->buckets[hashcode % NUM_DESCRIPTOR_SET_LAYOUT_BUCKETS]; - - DescriptorSetLayoutHashMap map; - map.key = key; - map.value = value; - - EXPAND_ELEMENTS_IF_NEEDED(arr, 4, DescriptorSetLayoutHashMap); - - arr->elements[arr->count] = map; - arr->count += 1; -} - -typedef struct RenderPassColorTargetDescription -{ - VkFormat format; - Refresh_Vec4 clearColor; - Refresh_LoadOp loadOp; - Refresh_StoreOp storeOp; -} RenderPassColorTargetDescription; - -typedef struct RenderPassDepthStencilTargetDescription -{ - VkFormat format; - Refresh_LoadOp loadOp; - Refresh_StoreOp storeOp; - Refresh_LoadOp stencilLoadOp; - Refresh_StoreOp stencilStoreOp; -} RenderPassDepthStencilTargetDescription; - -typedef struct RenderPassHash -{ - RenderPassColorTargetDescription colorTargetDescriptions[MAX_COLOR_TARGET_BINDINGS]; - uint32_t colorAttachmentCount; - RenderPassDepthStencilTargetDescription depthStencilTargetDescription; - Refresh_SampleCount colorAttachmentSampleCount; -} RenderPassHash; - -typedef struct RenderPassHashMap -{ - RenderPassHash key; - VkRenderPass value; -} RenderPassHashMap; - -typedef struct RenderPassHashArray -{ - RenderPassHashMap *elements; - int32_t count; - int32_t capacity; -} RenderPassHashArray; - -static inline uint8_t RenderPassHash_Compare( - RenderPassHash *a, - RenderPassHash *b -) { - uint32_t i; - - if (a->colorAttachmentCount != b->colorAttachmentCount) - { - return 0; - } - - if (a->colorAttachmentSampleCount != b->colorAttachmentSampleCount) - { - return 0; - } - - for (i = 0; i < a->colorAttachmentCount; i += 1) - { - if (a->colorTargetDescriptions[i].format != b->colorTargetDescriptions[i].format) - { - return 0; - } - - if ( a->colorTargetDescriptions[i].clearColor.x != b->colorTargetDescriptions[i].clearColor.x || - a->colorTargetDescriptions[i].clearColor.y != b->colorTargetDescriptions[i].clearColor.y || - a->colorTargetDescriptions[i].clearColor.z != b->colorTargetDescriptions[i].clearColor.z || - a->colorTargetDescriptions[i].clearColor.w != b->colorTargetDescriptions[i].clearColor.w ) - { - return 0; - } - - if (a->colorTargetDescriptions[i].loadOp != b->colorTargetDescriptions[i].loadOp) - { - return 0; - } - - if (a->colorTargetDescriptions[i].storeOp != b->colorTargetDescriptions[i].storeOp) - { - return 0; - } - } - - if (a->depthStencilTargetDescription.format != b->depthStencilTargetDescription.format) - { - return 0; - } - - if (a->depthStencilTargetDescription.loadOp != b->depthStencilTargetDescription.loadOp) - { - return 0; - } - - if (a->depthStencilTargetDescription.storeOp != b->depthStencilTargetDescription.storeOp) - { - return 0; - } - - if (a->depthStencilTargetDescription.stencilLoadOp != b->depthStencilTargetDescription.stencilLoadOp) - { - return 0; - } - - if (a->depthStencilTargetDescription.stencilStoreOp != b->depthStencilTargetDescription.stencilStoreOp) - { - return 0; - } - - return 1; -} - -static inline VkRenderPass RenderPassHashArray_Fetch( - RenderPassHashArray *arr, - RenderPassHash *key -) { - int32_t i; - - for (i = 0; i < arr->count; i += 1) - { - RenderPassHash *e = &arr->elements[i].key; - - if (RenderPassHash_Compare(e, key)) - { - return arr->elements[i].value; - } - } - - return VK_NULL_HANDLE; -} - -static inline void RenderPassHashArray_Insert( - RenderPassHashArray *arr, - RenderPassHash key, - VkRenderPass value -) { - RenderPassHashMap map; - - map.key = key; - map.value = value; - - EXPAND_ELEMENTS_IF_NEEDED(arr, 4, RenderPassHashMap) - - arr->elements[arr->count] = map; - arr->count += 1; -} - -typedef struct FramebufferHash -{ - VkImageView colorAttachmentViews[MAX_COLOR_TARGET_BINDINGS]; - VkImageView colorMultiSampleAttachmentViews[MAX_COLOR_TARGET_BINDINGS]; - uint32_t colorAttachmentCount; - VkImageView depthStencilAttachmentView; - uint32_t width; - uint32_t height; -} FramebufferHash; - -typedef struct FramebufferHashMap -{ - FramebufferHash key; - VulkanFramebuffer *value; -} FramebufferHashMap; - -typedef struct FramebufferHashArray -{ - FramebufferHashMap *elements; - int32_t count; - int32_t capacity; -} FramebufferHashArray; - -static inline uint8_t FramebufferHash_Compare( - FramebufferHash *a, - FramebufferHash *b -) { - uint32_t i; - - if (a->colorAttachmentCount != b->colorAttachmentCount) - { - return 0; - } - - for (i = 0; i < a->colorAttachmentCount; i += 1) - { - if (a->colorAttachmentViews[i] != b->colorAttachmentViews[i]) - { - return 0; - } - - if (a->colorMultiSampleAttachmentViews[i] != b->colorMultiSampleAttachmentViews[i]) - { - return 0; - } - } - - if (a->depthStencilAttachmentView != b->depthStencilAttachmentView) - { - return 0; - } - - if (a->width != b->width) - { - return 0; - } - - if (a->height != b->height) - { - return 0; - } - - return 1; -} - -static inline VulkanFramebuffer* FramebufferHashArray_Fetch( - FramebufferHashArray *arr, - FramebufferHash *key -) { - int32_t i; - - for (i = 0; i < arr->count; i += 1) - { - FramebufferHash *e = &arr->elements[i].key; - if (FramebufferHash_Compare(e, key)) - { - return arr->elements[i].value; - } - } - - return VK_NULL_HANDLE; -} - -static inline void FramebufferHashArray_Insert( - FramebufferHashArray *arr, - FramebufferHash key, - VulkanFramebuffer *value -) { - FramebufferHashMap map; - map.key = key; - map.value = value; - - EXPAND_ELEMENTS_IF_NEEDED(arr, 4, FramebufferHashMap) - - arr->elements[arr->count] = map; - arr->count += 1; -} - -static inline void FramebufferHashArray_Remove( - FramebufferHashArray *arr, - uint32_t index -) { - if (index != arr->count - 1) - { - arr->elements[index] = arr->elements[arr->count - 1]; - } - - arr->count -= 1; -} - -typedef struct RenderTargetHash -{ - VulkanTexture *texture; - uint32_t depth; - uint32_t layer; - uint32_t level; -} RenderTargetHash; - -typedef struct RenderTargetHashMap -{ - RenderTargetHash key; - VulkanRenderTarget *value; -} RenderTargetHashMap; - -typedef struct RenderTargetHashArray -{ - RenderTargetHashMap *elements; - int32_t count; - int32_t capacity; -} RenderTargetHashArray; - -static inline uint8_t RenderTargetHash_Compare( - RenderTargetHash *a, - RenderTargetHash *b -) { - if (a->texture != b->texture) - { - return 0; - } - - if (a->layer != b->layer) - { - return 0; - } - - if (a->level != b->level) - { - return 0; - } - - if (a->depth != b->depth) - { - return 0; - } - - return 1; -} - -static inline VulkanRenderTarget* RenderTargetHash_Fetch( - RenderTargetHashArray *arr, - RenderTargetHash *key -) { - int32_t i; - - for (i = 0; i < arr->count; i += 1) - { - RenderTargetHash *e = &arr->elements[i].key; - if (RenderTargetHash_Compare(e, key)) - { - return arr->elements[i].value; - } - } - - return NULL; -} - -static inline void RenderTargetHash_Insert( - RenderTargetHashArray *arr, - RenderTargetHash key, - VulkanRenderTarget *value -) { - RenderTargetHashMap map; - map.key = key; - map.value = value; - - EXPAND_ELEMENTS_IF_NEEDED(arr, 4, RenderTargetHashMap) - - arr->elements[arr->count] = map; - arr->count += 1; -} - -static inline void RenderTargetHash_Remove( - RenderTargetHashArray *arr, - uint32_t index -) { - if (index != arr->count - 1) - { - arr->elements[index] = arr->elements[arr->count - 1]; - } - - arr->count -= 1; -} - -/* Descriptor Set Caches */ - -struct DescriptorSetCache -{ - SDL_mutex *lock; - VkDescriptorSetLayout descriptorSetLayout; - uint32_t bindingCount; - VkDescriptorType descriptorType; - - VkDescriptorPool *descriptorPools; - uint32_t descriptorPoolCount; - uint32_t nextPoolSize; - - VkDescriptorSet *inactiveDescriptorSets; - uint32_t inactiveDescriptorSetCount; - uint32_t inactiveDescriptorSetCapacity; -}; - -/* Pipeline Caches */ - -#define NUM_PIPELINE_LAYOUT_BUCKETS 1031 - -typedef struct GraphicsPipelineLayoutHash -{ - VkDescriptorSetLayout vertexSamplerLayout; - VkDescriptorSetLayout fragmentSamplerLayout; - VkDescriptorSetLayout vertexUniformLayout; - VkDescriptorSetLayout fragmentUniformLayout; -} GraphicsPipelineLayoutHash; - -typedef struct GraphicsPipelineLayoutHashMap -{ - GraphicsPipelineLayoutHash key; - VulkanGraphicsPipelineLayout *value; -} GraphicsPipelineLayoutHashMap; - -typedef struct GraphicsPipelineLayoutHashArray -{ - GraphicsPipelineLayoutHashMap *elements; - int32_t count; - int32_t capacity; -} GraphicsPipelineLayoutHashArray; - -typedef struct GraphicsPipelineLayoutHashTable -{ - GraphicsPipelineLayoutHashArray buckets[NUM_PIPELINE_LAYOUT_BUCKETS]; -} GraphicsPipelineLayoutHashTable; - -static inline uint64_t GraphicsPipelineLayoutHashTable_GetHashCode(GraphicsPipelineLayoutHash key) -{ - const uint64_t HASH_FACTOR = 97; - uint64_t result = 1; - result = result * HASH_FACTOR + (uint64_t) key.vertexSamplerLayout; - result = result * HASH_FACTOR + (uint64_t) key.fragmentSamplerLayout; - result = result * HASH_FACTOR + (uint64_t) key.vertexUniformLayout; - result = result * HASH_FACTOR + (uint64_t) key.fragmentUniformLayout; - return result; -} - -static inline VulkanGraphicsPipelineLayout* GraphicsPipelineLayoutHashArray_Fetch( - GraphicsPipelineLayoutHashTable *table, - GraphicsPipelineLayoutHash key -) { - int32_t i; - uint64_t hashcode = GraphicsPipelineLayoutHashTable_GetHashCode(key); - GraphicsPipelineLayoutHashArray *arr = &table->buckets[hashcode % NUM_PIPELINE_LAYOUT_BUCKETS]; - - for (i = 0; i < arr->count; i += 1) - { - const GraphicsPipelineLayoutHash *e = &arr->elements[i].key; - if ( key.vertexSamplerLayout == e->vertexSamplerLayout && - key.fragmentSamplerLayout == e->fragmentSamplerLayout && - key.vertexUniformLayout == e->vertexUniformLayout && - key.fragmentUniformLayout == e->fragmentUniformLayout ) - { - return arr->elements[i].value; - } - } - - return NULL; -} - -static inline void GraphicsPipelineLayoutHashArray_Insert( - GraphicsPipelineLayoutHashTable *table, - GraphicsPipelineLayoutHash key, - VulkanGraphicsPipelineLayout *value -) { - uint64_t hashcode = GraphicsPipelineLayoutHashTable_GetHashCode(key); - GraphicsPipelineLayoutHashArray *arr = &table->buckets[hashcode % NUM_PIPELINE_LAYOUT_BUCKETS]; - - GraphicsPipelineLayoutHashMap map; - map.key = key; - map.value = value; - - EXPAND_ELEMENTS_IF_NEEDED(arr, 4, GraphicsPipelineLayoutHashMap) - - arr->elements[arr->count] = map; - arr->count += 1; -} - -typedef struct ComputePipelineLayoutHash -{ - VkDescriptorSetLayout bufferLayout; - VkDescriptorSetLayout imageLayout; - VkDescriptorSetLayout uniformLayout; -} ComputePipelineLayoutHash; - -typedef struct ComputePipelineLayoutHashMap -{ - ComputePipelineLayoutHash key; - VulkanComputePipelineLayout *value; -} ComputePipelineLayoutHashMap; - -typedef struct ComputePipelineLayoutHashArray -{ - ComputePipelineLayoutHashMap *elements; - int32_t count; - int32_t capacity; -} ComputePipelineLayoutHashArray; - -typedef struct ComputePipelineLayoutHashTable -{ - ComputePipelineLayoutHashArray buckets[NUM_PIPELINE_LAYOUT_BUCKETS]; -} ComputePipelineLayoutHashTable; - -static inline uint64_t ComputePipelineLayoutHashTable_GetHashCode(ComputePipelineLayoutHash key) -{ - const uint64_t HASH_FACTOR = 97; - uint64_t result = 1; - result = result * HASH_FACTOR + (uint64_t) key.bufferLayout; - result = result * HASH_FACTOR + (uint64_t) key.imageLayout; - result = result * HASH_FACTOR + (uint64_t) key.uniformLayout; - return result; -} - -static inline VulkanComputePipelineLayout* ComputePipelineLayoutHashArray_Fetch( - ComputePipelineLayoutHashTable *table, - ComputePipelineLayoutHash key -) { - int32_t i; - uint64_t hashcode = ComputePipelineLayoutHashTable_GetHashCode(key); - ComputePipelineLayoutHashArray *arr = &table->buckets[hashcode % NUM_PIPELINE_LAYOUT_BUCKETS]; - - for (i = 0; i < arr->count; i += 1) - { - const ComputePipelineLayoutHash *e = &arr->elements[i].key; - if ( key.bufferLayout == e->bufferLayout && - key.imageLayout == e->imageLayout && - key.uniformLayout == e->uniformLayout ) - { - return arr->elements[i].value; - } - } - - return NULL; -} - -static inline void ComputePipelineLayoutHashArray_Insert( - ComputePipelineLayoutHashTable *table, - ComputePipelineLayoutHash key, - VulkanComputePipelineLayout *value -) { - uint64_t hashcode = ComputePipelineLayoutHashTable_GetHashCode(key); - ComputePipelineLayoutHashArray *arr = &table->buckets[hashcode % NUM_PIPELINE_LAYOUT_BUCKETS]; - - ComputePipelineLayoutHashMap map; - map.key = key; - map.value = value; - - EXPAND_ELEMENTS_IF_NEEDED(arr, 4, ComputePipelineLayoutHashMap) - - arr->elements[arr->count] = map; - arr->count += 1; -} - -/* Command structures */ - -typedef struct DescriptorSetData -{ - DescriptorSetCache *descriptorSetCache; - VkDescriptorSet descriptorSet; -} DescriptorSetData; - -typedef struct VulkanTransferBuffer -{ - VulkanBuffer* buffer; - VkDeviceSize offset; - uint8_t fromPool; -} VulkanTransferBuffer; - -typedef struct VulkanTransferBufferPool -{ - SDL_mutex *lock; - - VulkanTransferBuffer **availableBuffers; - uint32_t availableBufferCount; - uint32_t availableBufferCapacity; -} VulkanTransferBufferPool; - -typedef struct VulkanFencePool -{ - SDL_mutex *lock; - - VkFence *availableFences; - uint32_t availableFenceCount; - uint32_t availableFenceCapacity; -} VulkanFencePool; - -typedef struct VulkanCommandPool VulkanCommandPool; - -typedef struct VulkanCommandBuffer -{ - VkCommandBuffer commandBuffer; - VulkanCommandPool *commandPool; - - VulkanPresentData *presentDatas; - uint32_t presentDataCount; - uint32_t presentDataCapacity; - - VkSemaphore *waitSemaphores; - uint32_t waitSemaphoreCount; - uint32_t waitSemaphoreCapacity; - - VkSemaphore *signalSemaphores; - uint32_t signalSemaphoreCount; - uint32_t signalSemaphoreCapacity; - - VulkanComputePipeline *currentComputePipeline; - VulkanGraphicsPipeline *currentGraphicsPipeline; - - VulkanTexture *renderPassColorTargetTextures[MAX_COLOR_TARGET_BINDINGS]; - uint32_t renderPassColorTargetCount; - VulkanTexture *renderPassDepthTexture; /* can be NULL */ - - VulkanUniformBuffer *vertexUniformBuffer; - VulkanUniformBuffer *fragmentUniformBuffer; - VulkanUniformBuffer *computeUniformBuffer; - - VkDescriptorSet vertexSamplerDescriptorSet; /* updated by BindVertexSamplers */ - VkDescriptorSet fragmentSamplerDescriptorSet; /* updated by BindFragmentSamplers */ - VkDescriptorSet bufferDescriptorSet; /* updated by BindComputeBuffers */ - VkDescriptorSet imageDescriptorSet; /* updated by BindComputeTextures */ - - VulkanTransferBuffer** transferBuffers; - uint32_t transferBufferCount; - uint32_t transferBufferCapacity; - - VulkanUniformBuffer **boundUniformBuffers; - uint32_t boundUniformBufferCount; - uint32_t boundUniformBufferCapacity; - - DescriptorSetData *boundDescriptorSetDatas; - uint32_t boundDescriptorSetDataCount; - uint32_t boundDescriptorSetDataCapacity; - - /* Keep track of compute resources for memory barriers */ - - VulkanBuffer **boundComputeBuffers; - uint32_t boundComputeBufferCount; - uint32_t boundComputeBufferCapacity; - - VulkanTexture **boundComputeTextures; - uint32_t boundComputeTextureCount; - uint32_t boundComputeTextureCapacity; - - /* Viewport/scissor state */ - - VkViewport currentViewport; - VkRect2D currentScissor; - - /* Track used resources */ - - VulkanBuffer **usedBuffers; - uint32_t usedBufferCount; - uint32_t usedBufferCapacity; - - VulkanTexture **usedTextures; - uint32_t usedTextureCount; - uint32_t usedTextureCapacity; - - VulkanSampler **usedSamplers; - uint32_t usedSamplerCount; - uint32_t usedSamplerCapacity; - - VulkanGraphicsPipeline **usedGraphicsPipelines; - uint32_t usedGraphicsPipelineCount; - uint32_t usedGraphicsPipelineCapacity; - - VulkanComputePipeline **usedComputePipelines; - uint32_t usedComputePipelineCount; - uint32_t usedComputePipelineCapacity; - - VulkanFramebuffer **usedFramebuffers; - uint32_t usedFramebufferCount; - uint32_t usedFramebufferCapacity; - - /* Shader modules have references tracked by pipelines */ - - VkFence inFlightFence; - uint8_t autoReleaseFence; -} VulkanCommandBuffer; - -struct VulkanCommandPool -{ - SDL_threadID threadID; - VkCommandPool commandPool; - - VulkanCommandBuffer **inactiveCommandBuffers; - uint32_t inactiveCommandBufferCapacity; - uint32_t inactiveCommandBufferCount; -}; - -#define NUM_COMMAND_POOL_BUCKETS 1031 - -typedef struct CommandPoolHash -{ - SDL_threadID threadID; -} CommandPoolHash; - -typedef struct CommandPoolHashMap -{ - CommandPoolHash key; - VulkanCommandPool *value; -} CommandPoolHashMap; - -typedef struct CommandPoolHashArray -{ - CommandPoolHashMap *elements; - uint32_t count; - uint32_t capacity; -} CommandPoolHashArray; - -typedef struct CommandPoolHashTable -{ - CommandPoolHashArray buckets[NUM_COMMAND_POOL_BUCKETS]; -} CommandPoolHashTable; - -static inline uint64_t CommandPoolHashTable_GetHashCode(CommandPoolHash key) -{ - const uint64_t HASH_FACTOR = 97; - uint64_t result = 1; - result = result * HASH_FACTOR + (uint64_t) key.threadID; - return result; -} - -static inline VulkanCommandPool* CommandPoolHashTable_Fetch( - CommandPoolHashTable *table, - CommandPoolHash key -) { - uint32_t i; - uint64_t hashcode = CommandPoolHashTable_GetHashCode(key); - CommandPoolHashArray *arr = &table->buckets[hashcode % NUM_COMMAND_POOL_BUCKETS]; - - for (i = 0; i < arr->count; i += 1) - { - const CommandPoolHash *e = &arr->elements[i].key; - if (key.threadID == e->threadID) - { - return arr->elements[i].value; - } - } - - return NULL; -} - -static inline void CommandPoolHashTable_Insert( - CommandPoolHashTable *table, - CommandPoolHash key, - VulkanCommandPool *value -) { - uint64_t hashcode = CommandPoolHashTable_GetHashCode(key); - CommandPoolHashArray *arr = &table->buckets[hashcode % NUM_COMMAND_POOL_BUCKETS]; - - CommandPoolHashMap map; - map.key = key; - map.value = value; - - EXPAND_ELEMENTS_IF_NEEDED(arr, 4, CommandPoolHashMap) - - arr->elements[arr->count] = map; - arr->count += 1; -} - -/* Context */ - -typedef struct VulkanRenderer -{ - VkInstance instance; - VkPhysicalDevice physicalDevice; - VkPhysicalDeviceProperties2 physicalDeviceProperties; - VkPhysicalDeviceDriverPropertiesKHR physicalDeviceDriverProperties; - VkDevice logicalDevice; - uint8_t unifiedMemoryWarning; - - uint8_t supportsDebugUtils; - uint8_t debugMode; - VulkanExtensions supports; - - VulkanMemoryAllocator *memoryAllocator; - VkPhysicalDeviceMemoryProperties memoryProperties; - - WindowData **claimedWindows; - uint32_t claimedWindowCount; - uint32_t claimedWindowCapacity; - - uint32_t queueFamilyIndex; - VkQueue unifiedQueue; - - VulkanCommandBuffer **submittedCommandBuffers; - uint32_t submittedCommandBufferCount; - uint32_t submittedCommandBufferCapacity; - - VulkanTransferBufferPool transferBufferPool; - VulkanFencePool fencePool; - - CommandPoolHashTable commandPoolHashTable; - DescriptorSetLayoutHashTable descriptorSetLayoutHashTable; - GraphicsPipelineLayoutHashTable graphicsPipelineLayoutHashTable; - ComputePipelineLayoutHashTable computePipelineLayoutHashTable; - RenderPassHashArray renderPassHashArray; - FramebufferHashArray framebufferHashArray; - RenderTargetHashArray renderTargetHashArray; - - VkDescriptorPool defaultDescriptorPool; - - VkDescriptorSetLayout emptyVertexSamplerLayout; - VkDescriptorSetLayout emptyFragmentSamplerLayout; - VkDescriptorSetLayout emptyComputeBufferDescriptorSetLayout; - VkDescriptorSetLayout emptyComputeImageDescriptorSetLayout; - - VkDescriptorSet emptyVertexSamplerDescriptorSet; - VkDescriptorSet emptyFragmentSamplerDescriptorSet; - VkDescriptorSet emptyComputeBufferDescriptorSet; - VkDescriptorSet emptyComputeImageDescriptorSet; - - VulkanUniformBufferPool *vertexUniformBufferPool; - VulkanUniformBufferPool *fragmentUniformBufferPool; - VulkanUniformBufferPool *computeUniformBufferPool; - - VkDescriptorSetLayout vertexUniformDescriptorSetLayout; - VkDescriptorSetLayout fragmentUniformDescriptorSetLayout; - VkDescriptorSetLayout computeUniformDescriptorSetLayout; - - VulkanBuffer *dummyBuffer; - VulkanUniformBuffer *dummyVertexUniformBuffer; - VulkanUniformBuffer *dummyFragmentUniformBuffer; - VulkanUniformBuffer *dummyComputeUniformBuffer; - - VkDeviceSize minUBOAlignment; - - /* Some drivers don't support D16 for some reason. Fun! */ - VkFormat D16Format; - VkFormat D16S8Format; - - VulkanTexture **texturesToDestroy; - uint32_t texturesToDestroyCount; - uint32_t texturesToDestroyCapacity; - - VulkanBuffer **buffersToDestroy; - uint32_t buffersToDestroyCount; - uint32_t buffersToDestroyCapacity; - - VulkanSampler **samplersToDestroy; - uint32_t samplersToDestroyCount; - uint32_t samplersToDestroyCapacity; - - VulkanGraphicsPipeline **graphicsPipelinesToDestroy; - uint32_t graphicsPipelinesToDestroyCount; - uint32_t graphicsPipelinesToDestroyCapacity; - - VulkanComputePipeline **computePipelinesToDestroy; - uint32_t computePipelinesToDestroyCount; - uint32_t computePipelinesToDestroyCapacity; - - VulkanShaderModule **shaderModulesToDestroy; - uint32_t shaderModulesToDestroyCount; - uint32_t shaderModulesToDestroyCapacity; - - VulkanFramebuffer **framebuffersToDestroy; - uint32_t framebuffersToDestroyCount; - uint32_t framebuffersToDestroyCapacity; - - SDL_mutex *allocatorLock; - SDL_mutex *disposeLock; - SDL_mutex *submitLock; - SDL_mutex *acquireCommandBufferLock; - SDL_mutex *renderPassFetchLock; - SDL_mutex *framebufferFetchLock; - SDL_mutex *renderTargetFetchLock; - - uint8_t needDefrag; - uint64_t defragTimestamp; - uint8_t defragInProgress; - -#define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \ - vkfntype_##func func; - #define VULKAN_DEVICE_FUNCTION(ext, ret, func, params) \ - vkfntype_##func func; - #include "Refresh_Driver_Vulkan_vkfuncs.h" -} VulkanRenderer; - -/* Forward declarations */ - -static uint8_t VULKAN_INTERNAL_DefragmentMemory(VulkanRenderer *renderer); -static void VULKAN_INTERNAL_BeginCommandBuffer(VulkanRenderer *renderer, VulkanCommandBuffer *commandBuffer); -static void VULKAN_UnclaimWindow(Refresh_Renderer *driverData, void *windowHandle); -static void VULKAN_Wait(Refresh_Renderer *driverData); -static void VULKAN_Submit(Refresh_Renderer *driverData, Refresh_CommandBuffer *commandBuffer); -static void VULKAN_INTERNAL_DestroyRenderTarget(VulkanRenderer *renderer, VulkanRenderTarget *renderTarget); - -/* Error Handling */ - -static inline const char* VkErrorMessages(VkResult code) -{ - #define ERR_TO_STR(e) \ - case e: return #e; - switch (code) - { - ERR_TO_STR(VK_ERROR_OUT_OF_HOST_MEMORY) - ERR_TO_STR(VK_ERROR_OUT_OF_DEVICE_MEMORY) - ERR_TO_STR(VK_ERROR_FRAGMENTED_POOL) - ERR_TO_STR(VK_ERROR_OUT_OF_POOL_MEMORY) - ERR_TO_STR(VK_ERROR_INITIALIZATION_FAILED) - ERR_TO_STR(VK_ERROR_LAYER_NOT_PRESENT) - ERR_TO_STR(VK_ERROR_EXTENSION_NOT_PRESENT) - ERR_TO_STR(VK_ERROR_FEATURE_NOT_PRESENT) - ERR_TO_STR(VK_ERROR_TOO_MANY_OBJECTS) - ERR_TO_STR(VK_ERROR_DEVICE_LOST) - ERR_TO_STR(VK_ERROR_INCOMPATIBLE_DRIVER) - ERR_TO_STR(VK_ERROR_OUT_OF_DATE_KHR) - ERR_TO_STR(VK_ERROR_SURFACE_LOST_KHR) - ERR_TO_STR(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT) - ERR_TO_STR(VK_SUBOPTIMAL_KHR) - default: return "Unhandled VkResult!"; - } - #undef ERR_TO_STR -} - -static inline void LogVulkanResultAsError( - const char* vulkanFunctionName, - VkResult result -) { - if (result != VK_SUCCESS) - { - Refresh_LogError( - "%s: %s", - vulkanFunctionName, - VkErrorMessages(result) - ); - } -} - -static inline void LogVulkanResultAsWarn( - const char* vulkanFunctionName, - VkResult result -) { - if (result != VK_SUCCESS) - { - Refresh_LogWarn( - "%s: %s", - vulkanFunctionName, - VkErrorMessages(result) - ); - } -} - -#define VULKAN_ERROR_CHECK(res, fn, ret) \ - if (res != VK_SUCCESS) \ - { \ - Refresh_LogError("%s %s", #fn, VkErrorMessages(res)); \ - return ret; \ - } - -/* Utility */ - -static inline VkFormat RefreshToVK_DepthFormat( - VulkanRenderer* renderer, - Refresh_TextureFormat format -) { - switch (format) - { - case REFRESH_TEXTUREFORMAT_D16_UNORM: - return renderer->D16Format; - case REFRESH_TEXTUREFORMAT_D16_UNORM_S8_UINT: - return renderer->D16S8Format; - case REFRESH_TEXTUREFORMAT_D32_SFLOAT: - return VK_FORMAT_D32_SFLOAT; - case REFRESH_TEXTUREFORMAT_D32_SFLOAT_S8_UINT: - return VK_FORMAT_D32_SFLOAT_S8_UINT; - default: - return VK_FORMAT_UNDEFINED; - } -} - -static inline uint8_t IsRefreshDepthFormat(Refresh_TextureFormat format) -{ - switch (format) - { - case REFRESH_TEXTUREFORMAT_D16_UNORM: - case REFRESH_TEXTUREFORMAT_D32_SFLOAT: - case REFRESH_TEXTUREFORMAT_D16_UNORM_S8_UINT: - case REFRESH_TEXTUREFORMAT_D32_SFLOAT_S8_UINT: - return 1; - - default: - return 0; - } -} - -static inline uint8_t IsDepthFormat(VkFormat format) -{ - switch(format) - { - case VK_FORMAT_D16_UNORM: - case VK_FORMAT_D32_SFLOAT: - case VK_FORMAT_D16_UNORM_S8_UINT: - case VK_FORMAT_D32_SFLOAT_S8_UINT: - return 1; - - default: - return 0; - } -} - -static inline uint8_t IsStencilFormat(VkFormat format) -{ - switch(format) - { - case VK_FORMAT_D16_UNORM_S8_UINT: - case VK_FORMAT_D32_SFLOAT_S8_UINT: - return 1; - - default: - return 0; - } -} - -static inline uint32_t VULKAN_INTERNAL_BytesPerPixel(VkFormat format) -{ - switch (format) - { - case VK_FORMAT_R8_UNORM: - case VK_FORMAT_R8_UINT: - return 1; - case VK_FORMAT_R5G6B5_UNORM_PACK16: - case VK_FORMAT_B4G4R4A4_UNORM_PACK16: - case VK_FORMAT_A1R5G5B5_UNORM_PACK16: - case VK_FORMAT_R16_SFLOAT: - case VK_FORMAT_R8G8_SNORM: - case VK_FORMAT_R8G8_UINT: - case VK_FORMAT_R16_UINT: - case VK_FORMAT_D16_UNORM: - return 2; - case VK_FORMAT_D16_UNORM_S8_UINT: - return 3; - case VK_FORMAT_R8G8B8A8_UNORM: - case VK_FORMAT_B8G8R8A8_UNORM: - case VK_FORMAT_R32_SFLOAT: - case VK_FORMAT_R16G16_UNORM: - case VK_FORMAT_R16G16_SFLOAT: - case VK_FORMAT_R8G8B8A8_SNORM: - case VK_FORMAT_A2R10G10B10_UNORM_PACK32: - case VK_FORMAT_R8G8B8A8_UINT: - case VK_FORMAT_R16G16_UINT: - case VK_FORMAT_D32_SFLOAT: - return 4; - case VK_FORMAT_D32_SFLOAT_S8_UINT: - return 5; - case VK_FORMAT_R16G16B16A16_SFLOAT: - case VK_FORMAT_R16G16B16A16_UNORM: - case VK_FORMAT_R32G32_SFLOAT: - case VK_FORMAT_R16G16B16A16_UINT: - case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: - return 8; - case VK_FORMAT_R32G32B32A32_SFLOAT: - case VK_FORMAT_BC2_UNORM_BLOCK: - case VK_FORMAT_BC3_UNORM_BLOCK: - case VK_FORMAT_BC7_UNORM_BLOCK: - return 16; - default: - Refresh_LogError("Texture format not recognized!"); - return 0; - } -} - -static inline uint32_t VULKAN_INTERNAL_TextureBlockSize( - VkFormat format -) { - switch (format) - { - case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: - case VK_FORMAT_BC2_UNORM_BLOCK: - case VK_FORMAT_BC3_UNORM_BLOCK: - case VK_FORMAT_BC7_UNORM_BLOCK: - return 4; - case VK_FORMAT_R8G8B8A8_UNORM: - case VK_FORMAT_B8G8R8A8_UNORM: - case VK_FORMAT_R5G6B5_UNORM_PACK16: - case VK_FORMAT_A1R5G5B5_UNORM_PACK16: - case VK_FORMAT_B4G4R4A4_UNORM_PACK16: - case VK_FORMAT_A2R10G10B10_UNORM_PACK32: - case VK_FORMAT_R16G16_UNORM: - case VK_FORMAT_R16G16B16A16_UNORM: - case VK_FORMAT_R8_UNORM: - case VK_FORMAT_R8G8_SNORM: - case VK_FORMAT_R8G8B8A8_SNORM: - case VK_FORMAT_R16_SFLOAT: - case VK_FORMAT_R16G16_SFLOAT: - case VK_FORMAT_R16G16B16A16_SFLOAT: - case VK_FORMAT_R32_SFLOAT: - case VK_FORMAT_R32G32_SFLOAT: - case VK_FORMAT_R32G32B32A32_SFLOAT: - case VK_FORMAT_R8_UINT: - case VK_FORMAT_R8G8_UINT: - case VK_FORMAT_R8G8B8A8_UINT: - case VK_FORMAT_R16_UINT: - case VK_FORMAT_R16G16_UINT: - case VK_FORMAT_R16G16B16A16_UINT: - case VK_FORMAT_D16_UNORM: - case VK_FORMAT_D32_SFLOAT: - case VK_FORMAT_D16_UNORM_S8_UINT: - case VK_FORMAT_D32_SFLOAT_S8_UINT: - return 1; - default: - Refresh_LogError("Unrecognized texture format!"); - return 0; - } -} - -static inline VkDeviceSize VULKAN_INTERNAL_BytesPerImage( - uint32_t width, - uint32_t height, - VkFormat format -) { - uint32_t blockSize = VULKAN_INTERNAL_TextureBlockSize(format); - return (width * height * VULKAN_INTERNAL_BytesPerPixel(format)) / (blockSize * blockSize); -} - -static inline Refresh_SampleCount VULKAN_INTERNAL_GetMaxMultiSampleCount( - VulkanRenderer *renderer, - Refresh_SampleCount multiSampleCount -) { - VkSampleCountFlags flags = renderer->physicalDeviceProperties.properties.limits.framebufferColorSampleCounts; - Refresh_SampleCount maxSupported = REFRESH_SAMPLECOUNT_1; - - if (flags & VK_SAMPLE_COUNT_8_BIT) - { - maxSupported = REFRESH_SAMPLECOUNT_8; - } - else if (flags & VK_SAMPLE_COUNT_4_BIT) - { - maxSupported = REFRESH_SAMPLECOUNT_4; - } - else if (flags & VK_SAMPLE_COUNT_2_BIT) - { - maxSupported = REFRESH_SAMPLECOUNT_2; - } - - return SDL_min(multiSampleCount, maxSupported); -} - -/* Memory Management */ - -/* Vulkan: Memory Allocation */ - -static inline VkDeviceSize VULKAN_INTERNAL_NextHighestAlignment( - VkDeviceSize n, - VkDeviceSize align -) { - return align * ((n + align - 1) / align); -} - -static void VULKAN_INTERNAL_MakeMemoryUnavailable( - VulkanRenderer* renderer, - VulkanMemoryAllocation *allocation -) { - uint32_t i, j; - VulkanMemoryFreeRegion *freeRegion; - - allocation->availableForAllocation = 0; - - for (i = 0; i < allocation->freeRegionCount; i += 1) - { - freeRegion = allocation->freeRegions[i]; - - /* close the gap in the sorted list */ - if (allocation->allocator->sortedFreeRegionCount > 1) - { - for (j = freeRegion->sortedIndex; j < allocation->allocator->sortedFreeRegionCount - 1; j += 1) - { - allocation->allocator->sortedFreeRegions[j] = - allocation->allocator->sortedFreeRegions[j + 1]; - - allocation->allocator->sortedFreeRegions[j]->sortedIndex = j; - } - } - - allocation->allocator->sortedFreeRegionCount -= 1; - } -} - -static void VULKAN_INTERNAL_RemoveMemoryFreeRegion( - VulkanRenderer *renderer, - VulkanMemoryFreeRegion *freeRegion -) { - uint32_t i; - - SDL_LockMutex(renderer->allocatorLock); - - if (freeRegion->allocation->availableForAllocation) - { - /* close the gap in the sorted list */ - if (freeRegion->allocation->allocator->sortedFreeRegionCount > 1) - { - for (i = freeRegion->sortedIndex; i < freeRegion->allocation->allocator->sortedFreeRegionCount - 1; i += 1) - { - freeRegion->allocation->allocator->sortedFreeRegions[i] = - freeRegion->allocation->allocator->sortedFreeRegions[i + 1]; - - freeRegion->allocation->allocator->sortedFreeRegions[i]->sortedIndex = i; - } - } - - freeRegion->allocation->allocator->sortedFreeRegionCount -= 1; - } - - /* close the gap in the buffer list */ - if (freeRegion->allocation->freeRegionCount > 1 && freeRegion->allocationIndex != freeRegion->allocation->freeRegionCount - 1) - { - freeRegion->allocation->freeRegions[freeRegion->allocationIndex] = - freeRegion->allocation->freeRegions[freeRegion->allocation->freeRegionCount - 1]; - - freeRegion->allocation->freeRegions[freeRegion->allocationIndex]->allocationIndex = - freeRegion->allocationIndex; - } - - freeRegion->allocation->freeRegionCount -= 1; - - freeRegion->allocation->freeSpace -= freeRegion->size; - - SDL_free(freeRegion); - - SDL_UnlockMutex(renderer->allocatorLock); -} - -static void VULKAN_INTERNAL_NewMemoryFreeRegion( - VulkanRenderer *renderer, - VulkanMemoryAllocation *allocation, - VkDeviceSize offset, - VkDeviceSize size -) { - VulkanMemoryFreeRegion *newFreeRegion; - VkDeviceSize newOffset, newSize; - int32_t insertionIndex = 0; - int32_t i; - - SDL_LockMutex(renderer->allocatorLock); - - /* look for an adjacent region to merge */ - for (i = allocation->freeRegionCount - 1; i >= 0; i -= 1) - { - /* check left side */ - if (allocation->freeRegions[i]->offset + allocation->freeRegions[i]->size == offset) - { - newOffset = allocation->freeRegions[i]->offset; - newSize = allocation->freeRegions[i]->size + size; - - VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, allocation->freeRegions[i]); - VULKAN_INTERNAL_NewMemoryFreeRegion(renderer, allocation, newOffset, newSize); - - SDL_UnlockMutex(renderer->allocatorLock); - return; - } - - /* check right side */ - if (allocation->freeRegions[i]->offset == offset + size) - { - newOffset = offset; - newSize = allocation->freeRegions[i]->size + size; - - VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, allocation->freeRegions[i]); - VULKAN_INTERNAL_NewMemoryFreeRegion(renderer, allocation, newOffset, newSize); - - SDL_UnlockMutex(renderer->allocatorLock); - return; - } - } - - /* region is not contiguous with another free region, make a new one */ - allocation->freeRegionCount += 1; - if (allocation->freeRegionCount > allocation->freeRegionCapacity) - { - allocation->freeRegionCapacity *= 2; - allocation->freeRegions = SDL_realloc( - allocation->freeRegions, - sizeof(VulkanMemoryFreeRegion*) * allocation->freeRegionCapacity - ); - } - - newFreeRegion = SDL_malloc(sizeof(VulkanMemoryFreeRegion)); - newFreeRegion->offset = offset; - newFreeRegion->size = size; - newFreeRegion->allocation = allocation; - - allocation->freeSpace += size; - - allocation->freeRegions[allocation->freeRegionCount - 1] = newFreeRegion; - newFreeRegion->allocationIndex = allocation->freeRegionCount - 1; - - if (allocation->availableForAllocation) - { - for (i = 0; i < allocation->allocator->sortedFreeRegionCount; i += 1) - { - if (allocation->allocator->sortedFreeRegions[i]->size < size) - { - /* this is where the new region should go */ - break; - } - - insertionIndex += 1; - } - - if (allocation->allocator->sortedFreeRegionCount + 1 > allocation->allocator->sortedFreeRegionCapacity) - { - allocation->allocator->sortedFreeRegionCapacity *= 2; - allocation->allocator->sortedFreeRegions = SDL_realloc( - allocation->allocator->sortedFreeRegions, - sizeof(VulkanMemoryFreeRegion*) * allocation->allocator->sortedFreeRegionCapacity - ); - } - - /* perform insertion sort */ - if (allocation->allocator->sortedFreeRegionCount > 0 && insertionIndex != allocation->allocator->sortedFreeRegionCount) - { - for (i = allocation->allocator->sortedFreeRegionCount; i > insertionIndex && i > 0; i -= 1) - { - allocation->allocator->sortedFreeRegions[i] = allocation->allocator->sortedFreeRegions[i - 1]; - allocation->allocator->sortedFreeRegions[i]->sortedIndex = i; - } - } - - allocation->allocator->sortedFreeRegionCount += 1; - allocation->allocator->sortedFreeRegions[insertionIndex] = newFreeRegion; - newFreeRegion->sortedIndex = insertionIndex; - } - - SDL_UnlockMutex(renderer->allocatorLock); -} - -static VulkanMemoryUsedRegion* VULKAN_INTERNAL_NewMemoryUsedRegion( - VulkanRenderer *renderer, - VulkanMemoryAllocation *allocation, - VkDeviceSize offset, - VkDeviceSize size, - VkDeviceSize resourceOffset, - VkDeviceSize resourceSize, - VkDeviceSize alignment -) { - VulkanMemoryUsedRegion *memoryUsedRegion; - - SDL_LockMutex(renderer->allocatorLock); - - if (allocation->usedRegionCount == allocation->usedRegionCapacity) - { - allocation->usedRegionCapacity *= 2; - allocation->usedRegions = SDL_realloc( - allocation->usedRegions, - allocation->usedRegionCapacity * sizeof(VulkanMemoryUsedRegion*) - ); - } - - memoryUsedRegion = SDL_malloc(sizeof(VulkanMemoryUsedRegion)); - memoryUsedRegion->allocation = allocation; - memoryUsedRegion->offset = offset; - memoryUsedRegion->size = size; - memoryUsedRegion->resourceOffset = resourceOffset; - memoryUsedRegion->resourceSize = resourceSize; - memoryUsedRegion->alignment = alignment; - - allocation->usedSpace += size; - - allocation->usedRegions[allocation->usedRegionCount] = memoryUsedRegion; - allocation->usedRegionCount += 1; - - SDL_UnlockMutex(renderer->allocatorLock); - - return memoryUsedRegion; -} - -static void VULKAN_INTERNAL_RemoveMemoryUsedRegion( - VulkanRenderer *renderer, - VulkanMemoryUsedRegion *usedRegion -) { - uint32_t i; - - SDL_LockMutex(renderer->allocatorLock); - - for (i = 0; i < usedRegion->allocation->usedRegionCount; i += 1) - { - if (usedRegion->allocation->usedRegions[i] == usedRegion) - { - /* plug the hole */ - if (i != usedRegion->allocation->usedRegionCount - 1) - { - usedRegion->allocation->usedRegions[i] = usedRegion->allocation->usedRegions[usedRegion->allocation->usedRegionCount - 1]; - } - - break; - } - } - - usedRegion->allocation->usedSpace -= usedRegion->size; - - usedRegion->allocation->usedRegionCount -= 1; - - VULKAN_INTERNAL_NewMemoryFreeRegion( - renderer, - usedRegion->allocation, - usedRegion->offset, - usedRegion->size - ); - - if (!usedRegion->allocation->dedicated) - { - renderer->needDefrag = 1; - renderer->defragTimestamp = SDL_GetTicks64() + DEFRAG_TIME; /* reset timer so we batch defrags */ - } - - SDL_free(usedRegion); - - SDL_UnlockMutex(renderer->allocatorLock); -} - -static uint8_t VULKAN_INTERNAL_FindMemoryType( - VulkanRenderer *renderer, - uint32_t typeFilter, - VkMemoryPropertyFlags requiredProperties, - VkMemoryPropertyFlags ignoredProperties, - uint32_t *memoryTypeIndex -) { - uint32_t i; - - for (i = *memoryTypeIndex; i < renderer->memoryProperties.memoryTypeCount; i += 1) - { - if ( (typeFilter & (1 << i)) && - (renderer->memoryProperties.memoryTypes[i].propertyFlags & requiredProperties) == requiredProperties && - (renderer->memoryProperties.memoryTypes[i].propertyFlags & ignoredProperties) == 0 ) - { - *memoryTypeIndex = i; - return 1; - } - } - - return 0; -} - -static uint8_t VULKAN_INTERNAL_FindBufferMemoryRequirements( - VulkanRenderer *renderer, - VkBuffer buffer, - VkMemoryPropertyFlags requiredMemoryProperties, - VkMemoryPropertyFlags ignoredMemoryProperties, - VkMemoryRequirements2KHR *pMemoryRequirements, - uint32_t *pMemoryTypeIndex -) { - VkBufferMemoryRequirementsInfo2KHR bufferRequirementsInfo; - bufferRequirementsInfo.sType = - VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR; - bufferRequirementsInfo.pNext = NULL; - bufferRequirementsInfo.buffer = buffer; - - renderer->vkGetBufferMemoryRequirements2KHR( - renderer->logicalDevice, - &bufferRequirementsInfo, - pMemoryRequirements - ); - - return VULKAN_INTERNAL_FindMemoryType( - renderer, - pMemoryRequirements->memoryRequirements.memoryTypeBits, - requiredMemoryProperties, - ignoredMemoryProperties, - pMemoryTypeIndex - ); -} - -static uint8_t VULKAN_INTERNAL_FindImageMemoryRequirements( - VulkanRenderer *renderer, - VkImage image, - VkMemoryPropertyFlags requiredMemoryPropertyFlags, - VkMemoryPropertyFlags ignoredMemoryPropertyFlags, - VkMemoryRequirements2KHR *pMemoryRequirements, - uint32_t *pMemoryTypeIndex -) { - VkImageMemoryRequirementsInfo2KHR imageRequirementsInfo; - imageRequirementsInfo.sType = - VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR; - imageRequirementsInfo.pNext = NULL; - imageRequirementsInfo.image = image; - - renderer->vkGetImageMemoryRequirements2KHR( - renderer->logicalDevice, - &imageRequirementsInfo, - pMemoryRequirements - ); - - return VULKAN_INTERNAL_FindMemoryType( - renderer, - pMemoryRequirements->memoryRequirements.memoryTypeBits, - requiredMemoryPropertyFlags, - ignoredMemoryPropertyFlags, - pMemoryTypeIndex - ); -} - -static void VULKAN_INTERNAL_DeallocateMemory( - VulkanRenderer *renderer, - VulkanMemorySubAllocator *allocator, - uint32_t allocationIndex -) { - uint32_t i; - - VulkanMemoryAllocation *allocation = allocator->allocations[allocationIndex]; - - SDL_LockMutex(renderer->allocatorLock); - - for (i = 0; i < allocation->freeRegionCount; i += 1) - { - VULKAN_INTERNAL_RemoveMemoryFreeRegion( - renderer, - allocation->freeRegions[i] - ); - } - SDL_free(allocation->freeRegions); - - /* no need to iterate used regions because deallocate - * only happens when there are 0 used regions - */ - SDL_free(allocation->usedRegions); - - renderer->vkFreeMemory( - renderer->logicalDevice, - allocation->memory, - NULL - ); - - SDL_DestroyMutex(allocation->memoryLock); - SDL_free(allocation); - - if (allocationIndex != allocator->allocationCount - 1) - { - allocator->allocations[allocationIndex] = allocator->allocations[allocator->allocationCount - 1]; - } - - allocator->allocationCount -= 1; - - SDL_UnlockMutex(renderer->allocatorLock); -} - -static uint8_t VULKAN_INTERNAL_AllocateMemory( - VulkanRenderer *renderer, - VkBuffer buffer, - VkImage image, - uint32_t memoryTypeIndex, - VkDeviceSize allocationSize, - uint8_t dedicated, /* indicates that one resource uses this memory and the memory shouldn't be moved */ - uint8_t isHostVisible, - VulkanMemoryAllocation **pMemoryAllocation) -{ - VulkanMemoryAllocation *allocation; - VulkanMemorySubAllocator *allocator = &renderer->memoryAllocator->subAllocators[memoryTypeIndex]; - VkMemoryAllocateInfo allocInfo; - VkResult result; - - allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - allocInfo.pNext = NULL; - allocInfo.memoryTypeIndex = memoryTypeIndex; - allocInfo.allocationSize = allocationSize; - - allocation = SDL_malloc(sizeof(VulkanMemoryAllocation)); - allocation->size = allocationSize; - allocation->freeSpace = 0; /* added by FreeRegions */ - allocation->usedSpace = 0; /* added by UsedRegions */ - allocation->memoryLock = SDL_CreateMutex(); - - allocator->allocationCount += 1; - allocator->allocations = SDL_realloc( - allocator->allocations, - sizeof(VulkanMemoryAllocation*) * allocator->allocationCount - ); - - allocator->allocations[ - allocator->allocationCount - 1 - ] = allocation; - - if (dedicated) - { - allocation->dedicated = 1; - allocation->availableForAllocation = 0; - } - else - { - allocInfo.pNext = NULL; - allocation->dedicated = 0; - allocation->availableForAllocation = 1; - } - - allocation->usedRegions = SDL_malloc(sizeof(VulkanMemoryUsedRegion*)); - allocation->usedRegionCount = 0; - allocation->usedRegionCapacity = 1; - - allocation->freeRegions = SDL_malloc(sizeof(VulkanMemoryFreeRegion*)); - allocation->freeRegionCount = 0; - allocation->freeRegionCapacity = 1; - - allocation->allocator = allocator; - - result = renderer->vkAllocateMemory( - renderer->logicalDevice, - &allocInfo, - NULL, - &allocation->memory - ); - - if (result != VK_SUCCESS) - { - /* Uh oh, we couldn't allocate, time to clean up */ - SDL_free(allocation->freeRegions); - - allocator->allocationCount -= 1; - allocator->allocations = SDL_realloc( - allocator->allocations, - sizeof(VulkanMemoryAllocation*) * allocator->allocationCount - ); - - SDL_free(allocation); - - return 0; - } - - /* persistent mapping for host memory */ - if (isHostVisible) - { - result = renderer->vkMapMemory( - renderer->logicalDevice, - allocation->memory, - 0, - VK_WHOLE_SIZE, - 0, - (void**) &allocation->mapPointer - ); - VULKAN_ERROR_CHECK(result, vkMapMemory, 0) - } - else - { - allocation->mapPointer = NULL; - } - - VULKAN_INTERNAL_NewMemoryFreeRegion( - renderer, - allocation, - 0, - allocation->size - ); - - *pMemoryAllocation = allocation; - return 1; -} - -static uint8_t VULKAN_INTERNAL_BindBufferMemory( - VulkanRenderer *renderer, - VulkanMemoryUsedRegion *usedRegion, - VkDeviceSize alignedOffset, - VkBuffer buffer -) { - VkResult vulkanResult; - - SDL_LockMutex(usedRegion->allocation->memoryLock); - - vulkanResult = renderer->vkBindBufferMemory( - renderer->logicalDevice, - buffer, - usedRegion->allocation->memory, - alignedOffset - ); - - SDL_UnlockMutex(usedRegion->allocation->memoryLock); - - VULKAN_ERROR_CHECK(vulkanResult, vkBindBufferMemory, 0) - - return 1; -} - -static uint8_t VULKAN_INTERNAL_BindImageMemory( - VulkanRenderer *renderer, - VulkanMemoryUsedRegion *usedRegion, - VkDeviceSize alignedOffset, - VkImage image -) { - VkResult vulkanResult; - - SDL_LockMutex(usedRegion->allocation->memoryLock); - - vulkanResult = renderer->vkBindImageMemory( - renderer->logicalDevice, - image, - usedRegion->allocation->memory, - alignedOffset - ); - - SDL_UnlockMutex(usedRegion->allocation->memoryLock); - - VULKAN_ERROR_CHECK(vulkanResult, vkBindBufferMemory, 0) - - return 1; -} - -static uint8_t VULKAN_INTERNAL_BindResourceMemory( - VulkanRenderer* renderer, - uint32_t memoryTypeIndex, - VkMemoryRequirements2KHR* memoryRequirements, - uint8_t forceDedicated, - VkDeviceSize resourceSize, /* may be different from requirements size! */ - VkBuffer buffer, /* may be VK_NULL_HANDLE */ - VkImage image, /* may be VK_NULL_HANDLE */ - VulkanMemoryUsedRegion** pMemoryUsedRegion -) { - VulkanMemoryAllocation *allocation; - VulkanMemorySubAllocator *allocator; - VulkanMemoryFreeRegion *region; - VulkanMemoryUsedRegion *usedRegion; - - VkDeviceSize requiredSize, allocationSize; - VkDeviceSize alignedOffset; - uint32_t newRegionSize, newRegionOffset; - uint8_t shouldAllocDedicated = forceDedicated; - uint8_t isHostVisible, allocationResult; - - isHostVisible = - (renderer->memoryProperties.memoryTypes[memoryTypeIndex].propertyFlags & - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; - - allocator = &renderer->memoryAllocator->subAllocators[memoryTypeIndex]; - requiredSize = memoryRequirements->memoryRequirements.size; - - if ( (buffer == VK_NULL_HANDLE && image == VK_NULL_HANDLE) || - (buffer != VK_NULL_HANDLE && image != VK_NULL_HANDLE) ) - { - Refresh_LogError("BindResourceMemory must be given either a VulkanBuffer or a VulkanTexture"); - return 0; - } - - SDL_LockMutex(renderer->allocatorLock); - - /* find the largest free region and use it */ - if (!shouldAllocDedicated && allocator->sortedFreeRegionCount > 0) - { - region = allocator->sortedFreeRegions[0]; - allocation = region->allocation; - - alignedOffset = VULKAN_INTERNAL_NextHighestAlignment( - region->offset, - memoryRequirements->memoryRequirements.alignment - ); - - if (alignedOffset + requiredSize <= region->offset + region->size) - { - usedRegion = VULKAN_INTERNAL_NewMemoryUsedRegion( - renderer, - allocation, - region->offset, - requiredSize + (alignedOffset - region->offset), - alignedOffset, - resourceSize, - memoryRequirements->memoryRequirements.alignment - ); - - usedRegion->isBuffer = buffer != VK_NULL_HANDLE; - - newRegionSize = region->size - ((alignedOffset - region->offset) + requiredSize); - newRegionOffset = alignedOffset + requiredSize; - - /* remove and add modified region to re-sort */ - VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, region); - - /* if size is 0, no need to re-insert */ - if (newRegionSize != 0) - { - VULKAN_INTERNAL_NewMemoryFreeRegion( - renderer, - allocation, - newRegionOffset, - newRegionSize - ); - } - - SDL_UnlockMutex(renderer->allocatorLock); - - if (buffer != VK_NULL_HANDLE) - { - if (!VULKAN_INTERNAL_BindBufferMemory( - renderer, - usedRegion, - alignedOffset, - buffer - )) { - VULKAN_INTERNAL_RemoveMemoryUsedRegion( - renderer, - usedRegion - ); - - return 0; - } - } - else if (image != VK_NULL_HANDLE) - { - if (!VULKAN_INTERNAL_BindImageMemory( - renderer, - usedRegion, - alignedOffset, - image - )) { - VULKAN_INTERNAL_RemoveMemoryUsedRegion( - renderer, - usedRegion - ); - - return 0; - } - } - - *pMemoryUsedRegion = usedRegion; - return 1; - } - } - - /* No suitable free regions exist, allocate a new memory region */ - - if (shouldAllocDedicated) - { - allocationSize = requiredSize; - } - else if (requiredSize > allocator->nextAllocationSize) - { - /* allocate a page of required size aligned to ALLOCATION_INCREMENT increments */ - allocationSize = - VULKAN_INTERNAL_NextHighestAlignment(requiredSize, ALLOCATION_INCREMENT); - } - else - { - allocationSize = allocator->nextAllocationSize; - } - - allocationResult = VULKAN_INTERNAL_AllocateMemory( - renderer, - buffer, - image, - memoryTypeIndex, - allocationSize, - shouldAllocDedicated, - isHostVisible, - &allocation - ); - - /* Uh oh, we're out of memory */ - if (allocationResult == 0) - { - SDL_UnlockMutex(renderer->allocatorLock); - - /* Responsibility of the caller to handle being out of memory */ - return 2; - } - - usedRegion = VULKAN_INTERNAL_NewMemoryUsedRegion( - renderer, - allocation, - 0, - requiredSize, - 0, - resourceSize, - memoryRequirements->memoryRequirements.alignment - ); - - usedRegion->isBuffer = buffer != VK_NULL_HANDLE; - - region = allocation->freeRegions[0]; - - newRegionOffset = region->offset + requiredSize; - newRegionSize = region->size - requiredSize; - - VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, region); - - if (newRegionSize != 0) - { - VULKAN_INTERNAL_NewMemoryFreeRegion( - renderer, - allocation, - newRegionOffset, - newRegionSize - ); - } - - SDL_UnlockMutex(renderer->allocatorLock); - - if (buffer != VK_NULL_HANDLE) - { - if (!VULKAN_INTERNAL_BindBufferMemory( - renderer, - usedRegion, - 0, - buffer - )) { - VULKAN_INTERNAL_RemoveMemoryUsedRegion( - renderer, - usedRegion - ); - - return 0; - } - } - else if (image != VK_NULL_HANDLE) - { - if (!VULKAN_INTERNAL_BindImageMemory( - renderer, - usedRegion, - 0, - image - )) { - VULKAN_INTERNAL_RemoveMemoryUsedRegion( - renderer, - usedRegion - ); - - return 0; - } - } - - *pMemoryUsedRegion = usedRegion; - return 1; -} - -static uint8_t VULKAN_INTERNAL_BindMemoryForImage( - VulkanRenderer* renderer, - VkImage image, - uint8_t isRenderTarget, - VulkanMemoryUsedRegion** usedRegion -) { - uint8_t bindResult = 0; - uint32_t memoryTypeIndex = 0; - VkMemoryPropertyFlags requiredMemoryPropertyFlags; - VkMemoryPropertyFlags ignoredMemoryPropertyFlags; - VkMemoryRequirements2KHR memoryRequirements = - { - VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR, - NULL - }; - - /* Prefer GPU allocation */ - requiredMemoryPropertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - ignoredMemoryPropertyFlags = 0; - - while (VULKAN_INTERNAL_FindImageMemoryRequirements( - renderer, - image, - requiredMemoryPropertyFlags, - ignoredMemoryPropertyFlags, - &memoryRequirements, - &memoryTypeIndex - )) { - bindResult = VULKAN_INTERNAL_BindResourceMemory( - renderer, - memoryTypeIndex, - &memoryRequirements, - isRenderTarget, - memoryRequirements.memoryRequirements.size, - VK_NULL_HANDLE, - image, - usedRegion - ); - - if (bindResult == 1) - { - break; - } - else /* Bind failed, try the next device-local heap */ - { - memoryTypeIndex += 1; - } - } - - /* Bind _still_ failed, try again without device local */ - if (bindResult != 1) - { - memoryTypeIndex = 0; - requiredMemoryPropertyFlags = 0; - ignoredMemoryPropertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - - if (isRenderTarget) - { - Refresh_LogWarn("RenderTarget is allocated in host memory, pre-allocate your targets!"); - } - - Refresh_LogWarn("Out of device local memory, falling back to host memory"); - - while (VULKAN_INTERNAL_FindImageMemoryRequirements( - renderer, - image, - requiredMemoryPropertyFlags, - ignoredMemoryPropertyFlags, - &memoryRequirements, - &memoryTypeIndex - )) { - bindResult = VULKAN_INTERNAL_BindResourceMemory( - renderer, - memoryTypeIndex, - &memoryRequirements, - isRenderTarget, - memoryRequirements.memoryRequirements.size, - VK_NULL_HANDLE, - image, - usedRegion - ); - - if (bindResult == 1) - { - break; - } - else /* Bind failed, try the next heap */ - { - memoryTypeIndex += 1; - } - } - } - - return bindResult; -} - -static uint8_t VULKAN_INTERNAL_BindMemoryForBuffer( - VulkanRenderer* renderer, - VkBuffer buffer, - VkDeviceSize size, - uint8_t requireHostVisible, - uint8_t preferDeviceLocal, - uint8_t dedicatedAllocation, - VulkanMemoryUsedRegion** usedRegion -) { - uint8_t bindResult = 0; - uint32_t memoryTypeIndex = 0; - VkMemoryPropertyFlags requiredMemoryPropertyFlags = 0; - VkMemoryPropertyFlags ignoredMemoryPropertyFlags = 0; - VkMemoryRequirements2KHR memoryRequirements = - { - VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR, - NULL - }; - - if (requireHostVisible) - { - requiredMemoryPropertyFlags = - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - } - else - { - ignoredMemoryPropertyFlags = - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; - } - - if (preferDeviceLocal) - { - requiredMemoryPropertyFlags |= - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - } - ignoredMemoryPropertyFlags = 0; - - while (VULKAN_INTERNAL_FindBufferMemoryRequirements( - renderer, - buffer, - requiredMemoryPropertyFlags, - ignoredMemoryPropertyFlags, - &memoryRequirements, - &memoryTypeIndex - )) { - bindResult = VULKAN_INTERNAL_BindResourceMemory( - renderer, - memoryTypeIndex, - &memoryRequirements, - dedicatedAllocation, - size, - buffer, - VK_NULL_HANDLE, - usedRegion - ); - - if (bindResult == 1) - { - break; - } - else /* Bind failed, try the next device-local heap */ - { - memoryTypeIndex += 1; - } - } - - /* Bind failed, try again if originally preferred device local */ - if (bindResult != 1) - { - memoryTypeIndex = 0; - - requiredMemoryPropertyFlags = - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - - /* Follow-up for the warning logged by FindMemoryType */ - if (!renderer->unifiedMemoryWarning) - { - Refresh_LogWarn("No unified memory found, falling back to host memory"); - renderer->unifiedMemoryWarning = 1; - } - - while (VULKAN_INTERNAL_FindBufferMemoryRequirements( - renderer, - buffer, - requiredMemoryPropertyFlags, - ignoredMemoryPropertyFlags, - &memoryRequirements, - &memoryTypeIndex - )) { - bindResult = VULKAN_INTERNAL_BindResourceMemory( - renderer, - memoryTypeIndex, - &memoryRequirements, - dedicatedAllocation, - size, - buffer, - VK_NULL_HANDLE, - usedRegion - ); - - if (bindResult == 1) - { - break; - } - else /* Bind failed, try the next heap */ - { - memoryTypeIndex += 1; - } - } - } - - return bindResult; -} - -static uint8_t VULKAN_INTERNAL_FindAllocationToDefragment( - VulkanRenderer *renderer, - VulkanMemorySubAllocator *allocator, - uint32_t *allocationIndexToDefrag -) { - uint32_t i, j; - - for (i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) - { - *allocator = renderer->memoryAllocator->subAllocators[i]; - - for (j = 0; j < allocator->allocationCount; j += 1) - { - if (allocator->allocations[j]->availableForAllocation == 1 && allocator->allocations[j]->freeRegionCount > 1) - { - *allocationIndexToDefrag = j; - return 1; - } - } - } - - return 0; -} - -/* Memory Barriers */ - -static void VULKAN_INTERNAL_BufferMemoryBarrier( - VulkanRenderer *renderer, - VkCommandBuffer commandBuffer, - VulkanResourceAccessType nextResourceAccessType, - VulkanBuffer *buffer -) { - VkPipelineStageFlags srcStages = 0; - VkPipelineStageFlags dstStages = 0; - VkBufferMemoryBarrier memoryBarrier; - VulkanResourceAccessType prevAccess, nextAccess; - const VulkanResourceAccessInfo *prevAccessInfo, *nextAccessInfo; - - memoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; - memoryBarrier.pNext = NULL; - memoryBarrier.srcAccessMask = 0; - memoryBarrier.dstAccessMask = 0; - memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - memoryBarrier.buffer = buffer->buffer; - memoryBarrier.offset = 0; - memoryBarrier.size = buffer->size; - - prevAccess = buffer->resourceAccessType; - prevAccessInfo = &AccessMap[prevAccess]; - - srcStages |= prevAccessInfo->stageMask; - - if (prevAccess > RESOURCE_ACCESS_END_OF_READ) - { - memoryBarrier.srcAccessMask |= prevAccessInfo->accessMask; - } - - nextAccess = nextResourceAccessType; - nextAccessInfo = &AccessMap[nextAccess]; - - dstStages |= nextAccessInfo->stageMask; - - if (memoryBarrier.srcAccessMask != 0) - { - memoryBarrier.dstAccessMask |= nextAccessInfo->accessMask; - } - - if (srcStages == 0) - { - srcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - } - if (dstStages == 0) - { - dstStages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - - renderer->vkCmdPipelineBarrier( - commandBuffer, - srcStages, - dstStages, - 0, - 0, - NULL, - 1, - &memoryBarrier, - 0, - NULL - ); - - buffer->resourceAccessType = nextResourceAccessType; -} - -static void VULKAN_INTERNAL_ImageMemoryBarrier( - VulkanRenderer *renderer, - VkCommandBuffer commandBuffer, - VulkanResourceAccessType nextAccess, - VkImageAspectFlags aspectMask, - uint32_t baseLayer, - uint32_t layerCount, - uint32_t baseLevel, - uint32_t levelCount, - uint8_t discardContents, - VkImage image, - VulkanResourceAccessType *resourceAccessType -) { - VkPipelineStageFlags srcStages = 0; - VkPipelineStageFlags dstStages = 0; - VkImageMemoryBarrier memoryBarrier; - VulkanResourceAccessType prevAccess; - const VulkanResourceAccessInfo *pPrevAccessInfo, *pNextAccessInfo; - - memoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - memoryBarrier.pNext = NULL; - memoryBarrier.srcAccessMask = 0; - memoryBarrier.dstAccessMask = 0; - memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - memoryBarrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED; - memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - memoryBarrier.image = image; - memoryBarrier.subresourceRange.aspectMask = aspectMask; - memoryBarrier.subresourceRange.baseArrayLayer = baseLayer; - memoryBarrier.subresourceRange.layerCount = layerCount; - memoryBarrier.subresourceRange.baseMipLevel = baseLevel; - memoryBarrier.subresourceRange.levelCount = levelCount; - - prevAccess = *resourceAccessType; - pPrevAccessInfo = &AccessMap[prevAccess]; - - srcStages |= pPrevAccessInfo->stageMask; - - if (prevAccess > RESOURCE_ACCESS_END_OF_READ) - { - memoryBarrier.srcAccessMask |= pPrevAccessInfo->accessMask; - } - - if (discardContents) - { - memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - } - else - { - memoryBarrier.oldLayout = pPrevAccessInfo->imageLayout; - } - - pNextAccessInfo = &AccessMap[nextAccess]; - - dstStages |= pNextAccessInfo->stageMask; - - memoryBarrier.dstAccessMask |= pNextAccessInfo->accessMask; - memoryBarrier.newLayout = pNextAccessInfo->imageLayout; - - if (srcStages == 0) - { - srcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - } - if (dstStages == 0) - { - dstStages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - - renderer->vkCmdPipelineBarrier( - commandBuffer, - srcStages, - dstStages, - 0, - 0, - NULL, - 0, - NULL, - 1, - &memoryBarrier - ); - - *resourceAccessType = nextAccess; -} - -/* Resource tracking */ - -#define TRACK_RESOURCE(resource, type, array, count, capacity) \ - uint32_t i; \ - \ - for (i = 0; i < commandBuffer->count; i += 1) \ - { \ - if (commandBuffer->array[i] == resource) \ - { \ - return; \ - } \ - } \ - \ - if (commandBuffer->count == commandBuffer->capacity) \ - { \ - commandBuffer->capacity += 1; \ - commandBuffer->array = SDL_realloc( \ - commandBuffer->array, \ - commandBuffer->capacity * sizeof(type) \ - ); \ - } \ - commandBuffer->array[commandBuffer->count] = resource; \ - commandBuffer->count += 1; \ - \ - SDL_AtomicIncRef(&resource->referenceCount); - - -static void VULKAN_INTERNAL_TrackBuffer( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer, - VulkanBuffer *buffer -) { - TRACK_RESOURCE( - buffer, - VulkanBuffer*, - usedBuffers, - usedBufferCount, - usedBufferCapacity - ) -} - -static void VULKAN_INTERNAL_TrackTexture( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer, - VulkanTexture *texture -) { - TRACK_RESOURCE( - texture, - VulkanTexture*, - usedTextures, - usedTextureCount, - usedTextureCapacity - ) -} - -static void VULKAN_INTERNAL_TrackSampler( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer, - VulkanSampler *sampler -) { - TRACK_RESOURCE( - sampler, - VulkanSampler*, - usedSamplers, - usedSamplerCount, - usedSamplerCapacity - ) -} - -static void VULKAN_INTERNAL_TrackGraphicsPipeline( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer, - VulkanGraphicsPipeline *graphicsPipeline -) { - TRACK_RESOURCE( - graphicsPipeline, - VulkanGraphicsPipeline*, - usedGraphicsPipelines, - usedGraphicsPipelineCount, - usedGraphicsPipelineCapacity - ) -} - -static void VULKAN_INTERNAL_TrackComputePipeline( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer, - VulkanComputePipeline *computePipeline -) { - TRACK_RESOURCE( - computePipeline, - VulkanComputePipeline*, - usedComputePipelines, - usedComputePipelineCount, - usedComputePipelineCapacity - ) -} - -static void VULKAN_INTERNAL_TrackFramebuffer( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer, - VulkanFramebuffer *framebuffer -) { - TRACK_RESOURCE( - framebuffer, - VulkanFramebuffer*, - usedFramebuffers, - usedFramebufferCount, - usedFramebufferCapacity - ); -} - -#undef TRACK_RESOURCE - -/* Resource Disposal */ - -static void VULKAN_INTERNAL_QueueDestroyFramebuffer( - VulkanRenderer *renderer, - VulkanFramebuffer *framebuffer -) { - SDL_LockMutex(renderer->disposeLock); - - EXPAND_ARRAY_IF_NEEDED( - renderer->framebuffersToDestroy, - VulkanFramebuffer*, - renderer->framebuffersToDestroyCount + 1, - renderer->framebuffersToDestroyCapacity, - renderer->framebuffersToDestroyCapacity * 2 - ) - - renderer->framebuffersToDestroy[renderer->framebuffersToDestroyCount] = framebuffer; - renderer->framebuffersToDestroyCount += 1; - - SDL_UnlockMutex(renderer->disposeLock); -} - -static void VULKAN_INTERNAL_DestroyFramebuffer( - VulkanRenderer *renderer, - VulkanFramebuffer *framebuffer -) { - renderer->vkDestroyFramebuffer( - renderer->logicalDevice, - framebuffer->framebuffer, - NULL - ); - - SDL_free(framebuffer); -} - -static void VULKAN_INTERNAL_RemoveFramebuffersContainingView( - VulkanRenderer *renderer, - VkImageView view -) { - FramebufferHash *hash; - int32_t i, j; - - for (i = renderer->framebufferHashArray.count - 1; i >= 0; i -= 1) - { - hash = &renderer->framebufferHashArray.elements[i].key; - - for (j = 0; j < hash->colorAttachmentCount; j += 1) - { - if (hash->colorAttachmentViews[j] == view) - { - VULKAN_INTERNAL_QueueDestroyFramebuffer( - renderer, - renderer->framebufferHashArray.elements[i].value - ); - - FramebufferHashArray_Remove( - &renderer->framebufferHashArray, - i - ); - - break; - } - } - } -} - -static void VULKAN_INTERNAL_RemoveRenderTargetsContainingTexture( - VulkanRenderer *renderer, - VulkanTexture *texture -) { - RenderTargetHash *hash; - VkImageView *viewsToCheck; - int32_t viewsToCheckCount; - int32_t viewsToCheckCapacity; - int32_t i; - - viewsToCheckCapacity = 16; - viewsToCheckCount = 0; - viewsToCheck = SDL_malloc(sizeof(VkImageView) * viewsToCheckCapacity); - - SDL_LockMutex(renderer->renderTargetFetchLock); - - for (i = renderer->renderTargetHashArray.count - 1; i >= 0; i -= 1) - { - hash = &renderer->renderTargetHashArray.elements[i].key; - - if (hash->texture == texture) - { - EXPAND_ARRAY_IF_NEEDED( - viewsToCheck, - VkImageView, - viewsToCheckCount + 1, - viewsToCheckCapacity, - viewsToCheckCapacity * 2 - ); - - viewsToCheck[viewsToCheckCount] = renderer->renderTargetHashArray.elements[i].value->view; - viewsToCheckCount += 1; - - VULKAN_INTERNAL_DestroyRenderTarget( - renderer, - renderer->renderTargetHashArray.elements[i].value - ); - - RenderTargetHash_Remove( - &renderer->renderTargetHashArray, - i - ); - } - } - - SDL_UnlockMutex(renderer->renderTargetFetchLock); - - SDL_LockMutex(renderer->framebufferFetchLock); - - for (i = 0; i < viewsToCheckCount; i += 1) - { - VULKAN_INTERNAL_RemoveFramebuffersContainingView( - renderer, - viewsToCheck[i] - ); - } - - SDL_UnlockMutex(renderer->framebufferFetchLock); - - SDL_free(viewsToCheck); -} - -static void VULKAN_INTERNAL_DestroyTexture( - VulkanRenderer* renderer, - VulkanTexture* texture -) { - VULKAN_INTERNAL_RemoveRenderTargetsContainingTexture( - renderer, - texture - ); - - renderer->vkDestroyImageView( - renderer->logicalDevice, - texture->view, - NULL - ); - - renderer->vkDestroyImage( - renderer->logicalDevice, - texture->image, - NULL - ); - - VULKAN_INTERNAL_RemoveMemoryUsedRegion( - renderer, - texture->usedRegion - ); - - /* destroy the msaa texture, if there is one */ - if (texture->msaaTex != NULL) - { - VULKAN_INTERNAL_DestroyTexture( - renderer, - texture->msaaTex - ); - } - - SDL_free(texture); -} - -static void VULKAN_INTERNAL_DestroyRenderTarget( - VulkanRenderer *renderer, - VulkanRenderTarget *renderTarget -) { - renderer->vkDestroyImageView( - renderer->logicalDevice, - renderTarget->view, - NULL - ); - - SDL_free(renderTarget); -} - -static void VULKAN_INTERNAL_DestroyBuffer( - VulkanRenderer* renderer, - VulkanBuffer* buffer -) { - renderer->vkDestroyBuffer( - renderer->logicalDevice, - buffer->buffer, - NULL - ); - - VULKAN_INTERNAL_RemoveMemoryUsedRegion( - renderer, - buffer->usedRegion - ); - - SDL_free(buffer); -} - -static void VULKAN_INTERNAL_DestroyCommandPool( - VulkanRenderer *renderer, - VulkanCommandPool *commandPool -) { - uint32_t i; - VulkanCommandBuffer* commandBuffer; - - renderer->vkDestroyCommandPool( - renderer->logicalDevice, - commandPool->commandPool, - NULL - ); - - for (i = 0; i < commandPool->inactiveCommandBufferCount; i += 1) - { - commandBuffer = commandPool->inactiveCommandBuffers[i]; - - SDL_free(commandBuffer->presentDatas); - SDL_free(commandBuffer->waitSemaphores); - SDL_free(commandBuffer->signalSemaphores); - SDL_free(commandBuffer->transferBuffers); - SDL_free(commandBuffer->boundUniformBuffers); - SDL_free(commandBuffer->boundDescriptorSetDatas); - SDL_free(commandBuffer->boundComputeBuffers); - SDL_free(commandBuffer->boundComputeTextures); - SDL_free(commandBuffer->usedBuffers); - SDL_free(commandBuffer->usedTextures); - SDL_free(commandBuffer->usedSamplers); - SDL_free(commandBuffer->usedGraphicsPipelines); - SDL_free(commandBuffer->usedComputePipelines); - SDL_free(commandBuffer->usedFramebuffers); - - SDL_free(commandBuffer); - } - - SDL_free(commandPool->inactiveCommandBuffers); - SDL_free(commandPool); -} - -static void VULKAN_INTERNAL_DestroyGraphicsPipeline( - VulkanRenderer *renderer, - VulkanGraphicsPipeline *graphicsPipeline -) { - renderer->vkDestroyPipeline( - renderer->logicalDevice, - graphicsPipeline->pipeline, - NULL - ); - - SDL_AtomicDecRef(&graphicsPipeline->vertexShaderModule->referenceCount); - SDL_AtomicDecRef(&graphicsPipeline->fragmentShaderModule->referenceCount); - - SDL_free(graphicsPipeline); -} - -static void VULKAN_INTERNAL_DestroyComputePipeline( - VulkanRenderer *renderer, - VulkanComputePipeline *computePipeline -) { - renderer->vkDestroyPipeline( - renderer->logicalDevice, - computePipeline->pipeline, - NULL - ); - - SDL_AtomicDecRef(&computePipeline->computeShaderModule->referenceCount); - - SDL_free(computePipeline); -} - -static void VULKAN_INTERNAL_DestroyShaderModule( - VulkanRenderer *renderer, - VulkanShaderModule *vulkanShaderModule -) { - renderer->vkDestroyShaderModule( - renderer->logicalDevice, - vulkanShaderModule->shaderModule, - NULL - ); - - SDL_free(vulkanShaderModule); -} - -static void VULKAN_INTERNAL_DestroySampler( - VulkanRenderer *renderer, - VulkanSampler *vulkanSampler -) { - renderer->vkDestroySampler( - renderer->logicalDevice, - vulkanSampler->sampler, - NULL - ); - - SDL_free(vulkanSampler); -} - -static void VULKAN_INTERNAL_DestroySwapchain( - VulkanRenderer* renderer, - WindowData *windowData -) { - uint32_t i; - VulkanSwapchainData *swapchainData; - - if (windowData == NULL) - { - return; - } - - swapchainData = windowData->swapchainData; - - if (swapchainData == NULL) - { - return; - } - - for (i = 0; i < swapchainData->imageCount; i += 1) - { - VULKAN_INTERNAL_RemoveRenderTargetsContainingTexture( - renderer, - swapchainData->textureContainers[i].vulkanTexture - ); - - renderer->vkDestroyImageView( - renderer->logicalDevice, - swapchainData->textureContainers[i].vulkanTexture->view, - NULL - ); - - SDL_free(swapchainData->textureContainers[i].vulkanTexture); - } - - SDL_free(swapchainData->textureContainers); - - renderer->vkDestroySwapchainKHR( - renderer->logicalDevice, - swapchainData->swapchain, - NULL - ); - - renderer->vkDestroySurfaceKHR( - renderer->instance, - swapchainData->surface, - NULL - ); - - renderer->vkDestroySemaphore( - renderer->logicalDevice, - swapchainData->imageAvailableSemaphore, - NULL - ); - - renderer->vkDestroySemaphore( - renderer->logicalDevice, - swapchainData->renderFinishedSemaphore, - NULL - ); - - windowData->swapchainData = NULL; - SDL_free(swapchainData); -} - -static void VULKAN_INTERNAL_DestroyDescriptorSetCache( - VulkanRenderer *renderer, - DescriptorSetCache *cache -) { - uint32_t i; - - if (cache == NULL) - { - return; - } - - for (i = 0; i < cache->descriptorPoolCount; i += 1) - { - renderer->vkDestroyDescriptorPool( - renderer->logicalDevice, - cache->descriptorPools[i], - NULL - ); - } - - SDL_free(cache->descriptorPools); - SDL_free(cache->inactiveDescriptorSets); - SDL_DestroyMutex(cache->lock); - SDL_free(cache); -} - -/* Descriptor cache stuff */ - -static uint8_t VULKAN_INTERNAL_CreateDescriptorPool( - VulkanRenderer *renderer, - VkDescriptorType descriptorType, - uint32_t descriptorSetCount, - uint32_t descriptorCount, - VkDescriptorPool *pDescriptorPool -) { - VkResult vulkanResult; - - VkDescriptorPoolSize descriptorPoolSize; - VkDescriptorPoolCreateInfo descriptorPoolInfo; - - descriptorPoolSize.type = descriptorType; - descriptorPoolSize.descriptorCount = descriptorCount; - - descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - descriptorPoolInfo.pNext = NULL; - descriptorPoolInfo.flags = 0; - descriptorPoolInfo.maxSets = descriptorSetCount; - descriptorPoolInfo.poolSizeCount = 1; - descriptorPoolInfo.pPoolSizes = &descriptorPoolSize; - - vulkanResult = renderer->vkCreateDescriptorPool( - renderer->logicalDevice, - &descriptorPoolInfo, - NULL, - pDescriptorPool - ); - - if (vulkanResult != VK_SUCCESS) - { - LogVulkanResultAsError("vkCreateDescriptorPool", vulkanResult); - return 0; - } - - return 1; -} - -static uint8_t VULKAN_INTERNAL_AllocateDescriptorSets( - VulkanRenderer *renderer, - VkDescriptorPool descriptorPool, - VkDescriptorSetLayout descriptorSetLayout, - uint32_t descriptorSetCount, - VkDescriptorSet *descriptorSetArray -) { - VkResult vulkanResult; - uint32_t i; - VkDescriptorSetAllocateInfo descriptorSetAllocateInfo; - VkDescriptorSetLayout *descriptorSetLayouts = SDL_stack_alloc(VkDescriptorSetLayout, descriptorSetCount); - - for (i = 0; i < descriptorSetCount; i += 1) - { - descriptorSetLayouts[i] = descriptorSetLayout; - } - - descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - descriptorSetAllocateInfo.pNext = NULL; - descriptorSetAllocateInfo.descriptorPool = descriptorPool; - descriptorSetAllocateInfo.descriptorSetCount = descriptorSetCount; - descriptorSetAllocateInfo.pSetLayouts = descriptorSetLayouts; - - vulkanResult = renderer->vkAllocateDescriptorSets( - renderer->logicalDevice, - &descriptorSetAllocateInfo, - descriptorSetArray - ); - - if (vulkanResult != VK_SUCCESS) - { - LogVulkanResultAsError("vkAllocateDescriptorSets", vulkanResult); - SDL_stack_free(descriptorSetLayouts); - return 0; - } - - SDL_stack_free(descriptorSetLayouts); - return 1; -} - -static DescriptorSetCache* VULKAN_INTERNAL_CreateDescriptorSetCache( - VulkanRenderer *renderer, - VkDescriptorType descriptorType, - VkDescriptorSetLayout descriptorSetLayout, - uint32_t bindingCount -) { - DescriptorSetCache *descriptorSetCache = SDL_malloc(sizeof(DescriptorSetCache)); - - descriptorSetCache->lock = SDL_CreateMutex(); - - descriptorSetCache->descriptorSetLayout = descriptorSetLayout; - descriptorSetCache->bindingCount = bindingCount; - descriptorSetCache->descriptorType = descriptorType; - - descriptorSetCache->descriptorPools = SDL_malloc(sizeof(VkDescriptorPool)); - descriptorSetCache->descriptorPoolCount = 1; - descriptorSetCache->nextPoolSize = DESCRIPTOR_POOL_STARTING_SIZE * 2; - - VULKAN_INTERNAL_CreateDescriptorPool( - renderer, - descriptorType, - DESCRIPTOR_POOL_STARTING_SIZE, - DESCRIPTOR_POOL_STARTING_SIZE * bindingCount, - &descriptorSetCache->descriptorPools[0] - ); - - descriptorSetCache->inactiveDescriptorSetCapacity = DESCRIPTOR_POOL_STARTING_SIZE; - descriptorSetCache->inactiveDescriptorSetCount = DESCRIPTOR_POOL_STARTING_SIZE; - descriptorSetCache->inactiveDescriptorSets = SDL_malloc( - sizeof(VkDescriptorSet) * DESCRIPTOR_POOL_STARTING_SIZE - ); - - VULKAN_INTERNAL_AllocateDescriptorSets( - renderer, - descriptorSetCache->descriptorPools[0], - descriptorSetCache->descriptorSetLayout, - DESCRIPTOR_POOL_STARTING_SIZE, - descriptorSetCache->inactiveDescriptorSets - ); - - return descriptorSetCache; -} - -static VkDescriptorSetLayout VULKAN_INTERNAL_FetchDescriptorSetLayout( - VulkanRenderer *renderer, - VkDescriptorType descriptorType, - uint32_t bindingCount, - VkShaderStageFlagBits shaderStageFlagBit -) { - DescriptorSetLayoutHash descriptorSetLayoutHash; - VkDescriptorSetLayout descriptorSetLayout; - - VkDescriptorSetLayoutBinding setLayoutBindings[MAX_TEXTURE_SAMPLERS]; - VkDescriptorSetLayoutCreateInfo setLayoutCreateInfo; - - VkResult vulkanResult; - uint32_t i; - - if (bindingCount == 0) - { - if (shaderStageFlagBit == VK_SHADER_STAGE_VERTEX_BIT) - { - return renderer->emptyVertexSamplerLayout; - } - else if (shaderStageFlagBit == VK_SHADER_STAGE_FRAGMENT_BIT) - { - return renderer->emptyFragmentSamplerLayout; - } - else if (shaderStageFlagBit == VK_SHADER_STAGE_COMPUTE_BIT) - { - if (descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) - { - return renderer->emptyComputeBufferDescriptorSetLayout; - } - else if (descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) - { - return renderer->emptyComputeImageDescriptorSetLayout; - } - else - { - Refresh_LogError("Invalid descriptor type for compute shader: ", descriptorType); - return NULL_DESC_LAYOUT; - } - } - else - { - Refresh_LogError("Invalid shader stage flag bit: ", shaderStageFlagBit); - return NULL_DESC_LAYOUT; - } - } - - descriptorSetLayoutHash.descriptorType = descriptorType; - descriptorSetLayoutHash.bindingCount = bindingCount; - descriptorSetLayoutHash.stageFlag = shaderStageFlagBit; - - descriptorSetLayout = DescriptorSetLayoutHashTable_Fetch( - &renderer->descriptorSetLayoutHashTable, - descriptorSetLayoutHash - ); - - if (descriptorSetLayout != VK_NULL_HANDLE) - { - return descriptorSetLayout; - } - - for (i = 0; i < bindingCount; i += 1) - { - setLayoutBindings[i].binding = i; - setLayoutBindings[i].descriptorCount = 1; - setLayoutBindings[i].descriptorType = descriptorType; - setLayoutBindings[i].stageFlags = shaderStageFlagBit; - setLayoutBindings[i].pImmutableSamplers = NULL; - } - - setLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - setLayoutCreateInfo.pNext = NULL; - setLayoutCreateInfo.flags = 0; - setLayoutCreateInfo.bindingCount = bindingCount; - setLayoutCreateInfo.pBindings = setLayoutBindings; - - vulkanResult = renderer->vkCreateDescriptorSetLayout( - renderer->logicalDevice, - &setLayoutCreateInfo, - NULL, - &descriptorSetLayout - ); - - if (vulkanResult != VK_SUCCESS) - { - LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult); - return NULL_DESC_LAYOUT; - } - - DescriptorSetLayoutHashTable_Insert( - &renderer->descriptorSetLayoutHashTable, - descriptorSetLayoutHash, - descriptorSetLayout - ); - - return descriptorSetLayout; -} - -static VulkanGraphicsPipelineLayout* VULKAN_INTERNAL_FetchGraphicsPipelineLayout( - VulkanRenderer *renderer, - uint32_t vertexSamplerBindingCount, - uint32_t fragmentSamplerBindingCount -) { - VkDescriptorSetLayout setLayouts[4]; - - GraphicsPipelineLayoutHash pipelineLayoutHash; - VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo; - VkResult vulkanResult; - - VulkanGraphicsPipelineLayout *vulkanGraphicsPipelineLayout; - - pipelineLayoutHash.vertexSamplerLayout = VULKAN_INTERNAL_FetchDescriptorSetLayout( - renderer, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - vertexSamplerBindingCount, - VK_SHADER_STAGE_VERTEX_BIT - ); - - pipelineLayoutHash.fragmentSamplerLayout = VULKAN_INTERNAL_FetchDescriptorSetLayout( - renderer, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - fragmentSamplerBindingCount, - VK_SHADER_STAGE_FRAGMENT_BIT - ); - - pipelineLayoutHash.vertexUniformLayout = renderer->vertexUniformDescriptorSetLayout; - pipelineLayoutHash.fragmentUniformLayout = renderer->fragmentUniformDescriptorSetLayout; - - vulkanGraphicsPipelineLayout = GraphicsPipelineLayoutHashArray_Fetch( - &renderer->graphicsPipelineLayoutHashTable, - pipelineLayoutHash - ); - - if (vulkanGraphicsPipelineLayout != NULL) - { - return vulkanGraphicsPipelineLayout; - } - - vulkanGraphicsPipelineLayout = SDL_malloc(sizeof(VulkanGraphicsPipelineLayout)); - - setLayouts[0] = pipelineLayoutHash.vertexSamplerLayout; - setLayouts[1] = pipelineLayoutHash.fragmentSamplerLayout; - setLayouts[2] = renderer->vertexUniformDescriptorSetLayout; - setLayouts[3] = renderer->fragmentUniformDescriptorSetLayout; - - pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutCreateInfo.pNext = NULL; - pipelineLayoutCreateInfo.flags = 0; - pipelineLayoutCreateInfo.setLayoutCount = 4; - pipelineLayoutCreateInfo.pSetLayouts = setLayouts; - pipelineLayoutCreateInfo.pushConstantRangeCount = 0; - pipelineLayoutCreateInfo.pPushConstantRanges = NULL; - - vulkanResult = renderer->vkCreatePipelineLayout( - renderer->logicalDevice, - &pipelineLayoutCreateInfo, - NULL, - &vulkanGraphicsPipelineLayout->pipelineLayout - ); - - if (vulkanResult != VK_SUCCESS) - { - LogVulkanResultAsError("vkCreatePipelineLayout", vulkanResult); - return NULL; - } - - GraphicsPipelineLayoutHashArray_Insert( - &renderer->graphicsPipelineLayoutHashTable, - pipelineLayoutHash, - vulkanGraphicsPipelineLayout - ); - - /* If the binding count is 0 - * we can just bind the same descriptor set - * so no cache is needed - */ - - if (vertexSamplerBindingCount == 0) - { - vulkanGraphicsPipelineLayout->vertexSamplerDescriptorSetCache = NULL; - } - else - { - vulkanGraphicsPipelineLayout->vertexSamplerDescriptorSetCache = - VULKAN_INTERNAL_CreateDescriptorSetCache( - renderer, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - pipelineLayoutHash.vertexSamplerLayout, - vertexSamplerBindingCount - ); - } - - if (fragmentSamplerBindingCount == 0) - { - vulkanGraphicsPipelineLayout->fragmentSamplerDescriptorSetCache = NULL; - } - else - { - vulkanGraphicsPipelineLayout->fragmentSamplerDescriptorSetCache = - VULKAN_INTERNAL_CreateDescriptorSetCache( - renderer, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - pipelineLayoutHash.fragmentSamplerLayout, - fragmentSamplerBindingCount - ); - } - - return vulkanGraphicsPipelineLayout; -} - -/* Data Buffer */ - -static VulkanBuffer* VULKAN_INTERNAL_CreateBuffer( - VulkanRenderer *renderer, - VkDeviceSize size, - VulkanResourceAccessType resourceAccessType, - VkBufferUsageFlags usage, - uint8_t requireHostVisible, - uint8_t preferDeviceLocal, - uint8_t dedicatedAllocation -) { - VulkanBuffer* buffer; - VkResult vulkanResult; - VkBufferCreateInfo bufferCreateInfo; - uint8_t bindResult; - - buffer = SDL_malloc(sizeof(VulkanBuffer)); - - buffer->size = size; - buffer->resourceAccessType = resourceAccessType; - buffer->usage = usage; - buffer->requireHostVisible = requireHostVisible; - buffer->preferDeviceLocal = preferDeviceLocal; - - bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferCreateInfo.pNext = NULL; - bufferCreateInfo.flags = 0; - bufferCreateInfo.size = size; - bufferCreateInfo.usage = usage; - bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - bufferCreateInfo.queueFamilyIndexCount = 1; - bufferCreateInfo.pQueueFamilyIndices = &renderer->queueFamilyIndex; - - vulkanResult = renderer->vkCreateBuffer( - renderer->logicalDevice, - &bufferCreateInfo, - NULL, - &buffer->buffer - ); - VULKAN_ERROR_CHECK(vulkanResult, vkCreateBuffer, 0) - - bindResult = VULKAN_INTERNAL_BindMemoryForBuffer( - renderer, - buffer->buffer, - buffer->size, - buffer->requireHostVisible, - buffer->preferDeviceLocal, - dedicatedAllocation, - &buffer->usedRegion - ); - - if (bindResult != 1) - { - renderer->vkDestroyBuffer( - renderer->logicalDevice, - buffer->buffer, - NULL); - - return NULL; - } - - buffer->usedRegion->vulkanBuffer = buffer; /* lol */ - buffer->container = NULL; - - buffer->resourceAccessType = resourceAccessType; - - SDL_AtomicSet(&buffer->referenceCount, 0); - - return buffer; -} - -/* Uniform buffer functions */ - -static uint8_t VULKAN_INTERNAL_AddUniformDescriptorPool( - VulkanRenderer *renderer, - VulkanUniformDescriptorPool *vulkanUniformDescriptorPool -) { - vulkanUniformDescriptorPool->descriptorPools = SDL_realloc( - vulkanUniformDescriptorPool->descriptorPools, - sizeof(VkDescriptorPool) * (vulkanUniformDescriptorPool->descriptorPoolCount + 1) - ); - - if (!VULKAN_INTERNAL_CreateDescriptorPool( - renderer, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - DESCRIPTOR_POOL_STARTING_SIZE, - DESCRIPTOR_POOL_STARTING_SIZE, - &vulkanUniformDescriptorPool->descriptorPools[vulkanUniformDescriptorPool->descriptorPoolCount] - )) { - Refresh_LogError("Failed to create descriptor pool!"); - return 0; - } - - vulkanUniformDescriptorPool->descriptorPoolCount += 1; - vulkanUniformDescriptorPool->availableDescriptorSetCount += DESCRIPTOR_POOL_STARTING_SIZE; - - return 1; -} - -static VulkanUniformBufferPool* VULKAN_INTERNAL_CreateUniformBufferPool( - VulkanRenderer *renderer, - VulkanUniformBufferType uniformBufferType -) { - VulkanUniformBufferPool* uniformBufferPool = SDL_malloc(sizeof(VulkanUniformBufferPool)); - VulkanResourceAccessType resourceAccessType; - - if (uniformBufferType == UNIFORM_BUFFER_VERTEX) - { - resourceAccessType = RESOURCE_ACCESS_VERTEX_SHADER_READ_UNIFORM_BUFFER; - } - else if (uniformBufferType == UNIFORM_BUFFER_FRAGMENT) - { - resourceAccessType = RESOURCE_ACCESS_FRAGMENT_SHADER_READ_UNIFORM_BUFFER; - } - else if (uniformBufferType == UNIFORM_BUFFER_COMPUTE) - { - resourceAccessType = RESOURCE_ACCESS_COMPUTE_SHADER_READ_UNIFORM_BUFFER; - } - else - { - Refresh_LogError("Unrecognized uniform buffer type!"); - return 0; - } - - uniformBufferPool->buffer = VULKAN_INTERNAL_CreateBuffer( - renderer, - UBO_BUFFER_SIZE, - resourceAccessType, - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - 1, - 0, - 1 - ); - - uniformBufferPool->nextAvailableOffset = 0; - - uniformBufferPool->type = uniformBufferType; - uniformBufferPool->lock = SDL_CreateMutex(); - - uniformBufferPool->availableBufferCapacity = 16; - uniformBufferPool->availableBufferCount = 0; - uniformBufferPool->availableBuffers = SDL_malloc(uniformBufferPool->availableBufferCapacity * sizeof(VulkanUniformBuffer*)); - - uniformBufferPool->descriptorPool.availableDescriptorSetCount = 0; - uniformBufferPool->descriptorPool.descriptorPoolCount = 0; - uniformBufferPool->descriptorPool.descriptorPools = NULL; - - VULKAN_INTERNAL_AddUniformDescriptorPool(renderer, &uniformBufferPool->descriptorPool); - - return uniformBufferPool; -} - -static void VULKAN_INTERNAL_BindUniformBuffer( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer, - VulkanUniformBuffer *uniformBuffer -) { - if (commandBuffer->boundUniformBufferCount >= commandBuffer->boundUniformBufferCapacity) - { - commandBuffer->boundUniformBufferCapacity *= 2; - commandBuffer->boundUniformBuffers = SDL_realloc( - commandBuffer->boundUniformBuffers, - sizeof(VulkanUniformBuffer*) * commandBuffer->boundUniformBufferCapacity - ); - } - - commandBuffer->boundUniformBuffers[commandBuffer->boundUniformBufferCount] = uniformBuffer; - commandBuffer->boundUniformBufferCount += 1; -} - -/* Buffer indirection so we can cleanly defrag */ -static VulkanBufferContainer* VULKAN_INTERNAL_CreateBufferContainer( - VulkanRenderer *renderer, - uint32_t sizeInBytes, - VulkanResourceAccessType resourceAccessType, - VkBufferUsageFlags usageFlags -) { - VulkanBufferContainer* bufferContainer; - VulkanBuffer* buffer; - - /* always set transfer bits so we can defrag */ - usageFlags |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - - buffer = VULKAN_INTERNAL_CreateBuffer( - renderer, - sizeInBytes, - resourceAccessType, - usageFlags, - 0, - 1, - 0 - ); - - if (buffer == NULL) - { - Refresh_LogError("Failed to create buffer!"); - return NULL; - } - - bufferContainer = SDL_malloc(sizeof(VulkanBufferContainer)); - bufferContainer->vulkanBuffer = buffer; - buffer->container = bufferContainer; - - return (VulkanBufferContainer*) bufferContainer; -} - -static uint8_t VULKAN_INTERNAL_CreateUniformBuffer( - VulkanRenderer *renderer, - VulkanUniformBufferPool *bufferPool -) { - VkDescriptorSetLayout descriptorSetLayout; - - if (bufferPool->type == UNIFORM_BUFFER_VERTEX) - { - descriptorSetLayout = renderer->vertexUniformDescriptorSetLayout; - } - else if (bufferPool->type == UNIFORM_BUFFER_FRAGMENT) - { - descriptorSetLayout = renderer->fragmentUniformDescriptorSetLayout; - } - else if (bufferPool->type == UNIFORM_BUFFER_COMPUTE) - { - descriptorSetLayout = renderer->computeUniformDescriptorSetLayout; - } - else - { - Refresh_LogError("Unrecognized uniform buffer type!"); - return 0; - } - - VulkanUniformBuffer *uniformBuffer = SDL_malloc(sizeof(VulkanUniformBuffer)); - uniformBuffer->pool = bufferPool; - uniformBuffer->poolOffset = bufferPool->nextAvailableOffset; - uniformBuffer->offset = 0; - - bufferPool->nextAvailableOffset += VULKAN_INTERNAL_NextHighestAlignment(UBO_SECTION_SIZE, renderer->minUBOAlignment); - - if (bufferPool->nextAvailableOffset >= UBO_BUFFER_SIZE) - { - Refresh_LogError("Uniform buffer overflow!"); - SDL_free(uniformBuffer); - return 0; - } - - /* Allocate a descriptor set for the uniform buffer */ - - if (bufferPool->descriptorPool.availableDescriptorSetCount == 0) - { - if (!VULKAN_INTERNAL_AddUniformDescriptorPool( - renderer, - &bufferPool->descriptorPool - )) { - Refresh_LogError("Failed to add uniform descriptor pool!"); - SDL_free(uniformBuffer); - return 0; - } - } - - if (!VULKAN_INTERNAL_AllocateDescriptorSets( - renderer, - bufferPool->descriptorPool.descriptorPools[bufferPool->descriptorPool.descriptorPoolCount - 1], - descriptorSetLayout, - 1, - &uniformBuffer->descriptorSet - )) { - Refresh_LogError("Failed to allocate uniform descriptor set!"); - return 0; - } - - bufferPool->descriptorPool.availableDescriptorSetCount -= 1; - - if (bufferPool->availableBufferCount >= bufferPool->availableBufferCapacity) - { - bufferPool->availableBufferCapacity *= 2; - - bufferPool->availableBuffers = SDL_realloc( - bufferPool->availableBuffers, - sizeof(VulkanUniformBuffer*) * bufferPool->availableBufferCapacity - ); - } - - bufferPool->availableBuffers[bufferPool->availableBufferCount] = uniformBuffer; - bufferPool->availableBufferCount += 1; - - return 1; -} - -static VulkanUniformBuffer* VULKAN_INTERNAL_CreateDummyUniformBuffer( - VulkanRenderer *renderer, - VulkanUniformBufferType uniformBufferType -) { - VkDescriptorSetLayout descriptorSetLayout; - VkWriteDescriptorSet writeDescriptorSet; - VkDescriptorBufferInfo descriptorBufferInfo; - - if (uniformBufferType == UNIFORM_BUFFER_VERTEX) - { - descriptorSetLayout = renderer->vertexUniformDescriptorSetLayout; - } - else if (uniformBufferType == UNIFORM_BUFFER_FRAGMENT) - { - descriptorSetLayout = renderer->fragmentUniformDescriptorSetLayout; - } - else if (uniformBufferType == UNIFORM_BUFFER_COMPUTE) - { - descriptorSetLayout = renderer->computeUniformDescriptorSetLayout; - } - else - { - Refresh_LogError("Unrecognized uniform buffer type!"); - return NULL; - } - - VulkanUniformBuffer *uniformBuffer = SDL_malloc(sizeof(VulkanUniformBuffer)); - uniformBuffer->poolOffset = 0; - uniformBuffer->offset = 0; - - /* Allocate a descriptor set for the uniform buffer */ - - VULKAN_INTERNAL_AllocateDescriptorSets( - renderer, - renderer->defaultDescriptorPool, - descriptorSetLayout, - 1, - &uniformBuffer->descriptorSet - ); - - /* Update the descriptor set for the first and last time! */ - - descriptorBufferInfo.buffer = renderer->dummyBuffer->buffer; - descriptorBufferInfo.offset = 0; - descriptorBufferInfo.range = VK_WHOLE_SIZE; - - writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeDescriptorSet.pNext = NULL; - writeDescriptorSet.descriptorCount = 1; - writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - writeDescriptorSet.dstArrayElement = 0; - writeDescriptorSet.dstBinding = 0; - writeDescriptorSet.dstSet = uniformBuffer->descriptorSet; - writeDescriptorSet.pBufferInfo = &descriptorBufferInfo; - writeDescriptorSet.pImageInfo = NULL; - writeDescriptorSet.pTexelBufferView = NULL; - - renderer->vkUpdateDescriptorSets( - renderer->logicalDevice, - 1, - &writeDescriptorSet, - 0, - NULL - ); - - uniformBuffer->pool = NULL; /* No pool because this is a dummy */ - - return uniformBuffer; -} - -static void VULKAN_INTERNAL_DestroyUniformBufferPool( - VulkanRenderer *renderer, - VulkanUniformBufferPool *uniformBufferPool -) { - uint32_t i; - - for (i = 0; i < uniformBufferPool->descriptorPool.descriptorPoolCount; i += 1) - { - renderer->vkDestroyDescriptorPool( - renderer->logicalDevice, - uniformBufferPool->descriptorPool.descriptorPools[i], - NULL - ); - } - SDL_free(uniformBufferPool->descriptorPool.descriptorPools); - - /* This is always destroyed after submissions, so all buffers are available */ - for (i = 0; i < uniformBufferPool->availableBufferCount; i += 1) - { - SDL_free(uniformBufferPool->availableBuffers[i]); - } - - VULKAN_INTERNAL_DestroyBuffer(renderer, uniformBufferPool->buffer); - - SDL_DestroyMutex(uniformBufferPool->lock); - SDL_free(uniformBufferPool->availableBuffers); - SDL_free(uniformBufferPool); -} - -static VulkanUniformBuffer* VULKAN_INTERNAL_AcquireUniformBufferFromPool( - VulkanRenderer *renderer, - VulkanUniformBufferPool *bufferPool, - VkDeviceSize blockSize -) { - VkWriteDescriptorSet writeDescriptorSet; - VkDescriptorBufferInfo descriptorBufferInfo; - - SDL_LockMutex(bufferPool->lock); - - if (bufferPool->availableBufferCount == 0) - { - if (!VULKAN_INTERNAL_CreateUniformBuffer(renderer, bufferPool)) - { - SDL_UnlockMutex(bufferPool->lock); - Refresh_LogError("Failed to create uniform buffer!"); - return NULL; - } - } - - VulkanUniformBuffer *uniformBuffer = bufferPool->availableBuffers[bufferPool->availableBufferCount - 1]; - bufferPool->availableBufferCount -= 1; - - SDL_UnlockMutex(bufferPool->lock); - - uniformBuffer->offset = 0; - - /* Update the descriptor set with the correct range */ - - descriptorBufferInfo.buffer = uniformBuffer->pool->buffer->buffer; - descriptorBufferInfo.offset = uniformBuffer->poolOffset; - descriptorBufferInfo.range = blockSize; - - writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeDescriptorSet.pNext = NULL; - writeDescriptorSet.descriptorCount = 1; - writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - writeDescriptorSet.dstArrayElement = 0; - writeDescriptorSet.dstBinding = 0; - writeDescriptorSet.dstSet = uniformBuffer->descriptorSet; - writeDescriptorSet.pBufferInfo = &descriptorBufferInfo; - writeDescriptorSet.pImageInfo = NULL; - writeDescriptorSet.pTexelBufferView = NULL; - - renderer->vkUpdateDescriptorSets( - renderer->logicalDevice, - 1, - &writeDescriptorSet, - 0, - NULL - ); - - return uniformBuffer; -} - -/* Swapchain */ - -static uint8_t VULKAN_INTERNAL_QuerySwapChainSupport( - VulkanRenderer *renderer, - VkPhysicalDevice physicalDevice, - VkSurfaceKHR surface, - SwapChainSupportDetails *outputDetails -) { - VkResult result; - VkBool32 supportsPresent; - - renderer->vkGetPhysicalDeviceSurfaceSupportKHR( - physicalDevice, - renderer->queueFamilyIndex, - surface, - &supportsPresent - ); - - if (!supportsPresent) - { - Refresh_LogWarn("This surface does not support presenting!"); - return 0; - } - - /* Initialize these in case anything fails */ - outputDetails->formatsLength = 0; - outputDetails->presentModesLength = 0; - - /* Run the device surface queries */ - result = renderer->vkGetPhysicalDeviceSurfaceCapabilitiesKHR( - physicalDevice, - surface, - &outputDetails->capabilities - ); - VULKAN_ERROR_CHECK(result, vkGetPhysicalDeviceSurfaceCapabilitiesKHR, 0) - - if (!(outputDetails->capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) - { - Refresh_LogWarn("Opaque presentation unsupported! Expect weird transparency bugs!"); - } - - result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR( - physicalDevice, - surface, - &outputDetails->formatsLength, - NULL - ); - VULKAN_ERROR_CHECK(result, vkGetPhysicalDeviceSurfaceFormatsKHR, 0) - result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR( - physicalDevice, - surface, - &outputDetails->presentModesLength, - NULL - ); - VULKAN_ERROR_CHECK(result, vkGetPhysicalDeviceSurfacePresentModesKHR, 0) - - /* Generate the arrays, if applicable */ - if (outputDetails->formatsLength != 0) - { - outputDetails->formats = (VkSurfaceFormatKHR*) SDL_malloc( - sizeof(VkSurfaceFormatKHR) * outputDetails->formatsLength - ); - - if (!outputDetails->formats) - { - SDL_OutOfMemory(); - return 0; - } - - result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR( - physicalDevice, - surface, - &outputDetails->formatsLength, - outputDetails->formats - ); - if (result != VK_SUCCESS) - { - Refresh_LogError( - "vkGetPhysicalDeviceSurfaceFormatsKHR: %s", - VkErrorMessages(result) - ); - - SDL_free(outputDetails->formats); - return 0; - } - } - if (outputDetails->presentModesLength != 0) - { - outputDetails->presentModes = (VkPresentModeKHR*) SDL_malloc( - sizeof(VkPresentModeKHR) * outputDetails->presentModesLength - ); - - if (!outputDetails->presentModes) - { - SDL_OutOfMemory(); - return 0; - } - - result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR( - physicalDevice, - surface, - &outputDetails->presentModesLength, - outputDetails->presentModes - ); - if (result != VK_SUCCESS) - { - Refresh_LogError( - "vkGetPhysicalDeviceSurfacePresentModesKHR: %s", - VkErrorMessages(result) - ); - - SDL_free(outputDetails->formats); - SDL_free(outputDetails->presentModes); - return 0; - } - } - - /* If we made it here, all the queries were successfull. This does NOT - * necessarily mean there are any supported formats or present modes! - */ - return 1; -} - -static uint8_t VULKAN_INTERNAL_ChooseSwapSurfaceFormat( - VkFormat desiredFormat, - VkSurfaceFormatKHR *availableFormats, - uint32_t availableFormatsLength, - VkSurfaceFormatKHR *outputFormat -) { - uint32_t i; - for (i = 0; i < availableFormatsLength; i += 1) - { - if ( availableFormats[i].format == desiredFormat && - availableFormats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR ) - { - *outputFormat = availableFormats[i]; - return 1; - } - } - - return 0; -} - -static uint8_t VULKAN_INTERNAL_ChooseSwapPresentMode( - Refresh_PresentMode desiredPresentInterval, - VkPresentModeKHR *availablePresentModes, - uint32_t availablePresentModesLength, - VkPresentModeKHR *outputPresentMode -) { - #define CHECK_MODE(m) \ - for (i = 0; i < availablePresentModesLength; i += 1) \ - { \ - if (availablePresentModes[i] == m) \ - { \ - *outputPresentMode = m; \ - return 1; \ - } \ - } \ - - uint32_t i; - if (desiredPresentInterval == REFRESH_PRESENTMODE_IMMEDIATE) - { - CHECK_MODE(VK_PRESENT_MODE_IMMEDIATE_KHR) - } - else if (desiredPresentInterval == REFRESH_PRESENTMODE_MAILBOX) - { - CHECK_MODE(VK_PRESENT_MODE_MAILBOX_KHR) - } - else if (desiredPresentInterval == REFRESH_PRESENTMODE_FIFO) - { - CHECK_MODE(VK_PRESENT_MODE_FIFO_KHR) - } - else if (desiredPresentInterval == REFRESH_PRESENTMODE_FIFO_RELAXED) - { - CHECK_MODE(VK_PRESENT_MODE_FIFO_RELAXED_KHR) - } - else - { - Refresh_LogError( - "Unrecognized PresentInterval: %d", - desiredPresentInterval - ); - return 0; - } - - #undef CHECK_MODE - - *outputPresentMode = VK_PRESENT_MODE_FIFO_KHR; - return 1; -} - -static uint8_t VULKAN_INTERNAL_CreateSwapchain( - VulkanRenderer *renderer, - WindowData *windowData -) { - VkResult vulkanResult; - VulkanSwapchainData *swapchainData; - VkSwapchainCreateInfoKHR swapchainCreateInfo; - VkImage *swapchainImages; - VkImageViewCreateInfo imageViewCreateInfo; - VkSemaphoreCreateInfo semaphoreCreateInfo; - SwapChainSupportDetails swapchainSupportDetails; - int32_t drawableWidth, drawableHeight; - uint32_t i; - - swapchainData = SDL_malloc(sizeof(VulkanSwapchainData)); - - /* Each swapchain must have its own surface. */ - - if (!SDL_Vulkan_CreateSurface( - (SDL_Window*) windowData->windowHandle, - renderer->instance, - &swapchainData->surface - )) { - SDL_free(swapchainData); - Refresh_LogError( - "SDL_Vulkan_CreateSurface failed: %s", - SDL_GetError() - ); - return 0; - } - - if (!VULKAN_INTERNAL_QuerySwapChainSupport( - renderer, - renderer->physicalDevice, - swapchainData->surface, - &swapchainSupportDetails - )) { - renderer->vkDestroySurfaceKHR( - renderer->instance, - swapchainData->surface, - NULL - ); - if (swapchainSupportDetails.formatsLength > 0) - { - SDL_free(swapchainSupportDetails.formats); - } - if (swapchainSupportDetails.presentModesLength > 0) - { - SDL_free(swapchainSupportDetails.presentModes); - } - SDL_free(swapchainData); - Refresh_LogError("Device does not support swap chain creation"); - return 0; - } - - if ( swapchainSupportDetails.capabilities.currentExtent.width == 0 || - swapchainSupportDetails.capabilities.currentExtent.height == 0) - { - /* Not an error, just minimize behavior! */ - renderer->vkDestroySurfaceKHR( - renderer->instance, - swapchainData->surface, - NULL - ); - if (swapchainSupportDetails.formatsLength > 0) - { - SDL_free(swapchainSupportDetails.formats); - } - if (swapchainSupportDetails.presentModesLength > 0) - { - SDL_free(swapchainSupportDetails.presentModes); - } - SDL_free(swapchainData); - return 0; - } - - swapchainData->swapchainFormat = VK_FORMAT_R8G8B8A8_UNORM; - swapchainData->swapchainSwizzle.r = VK_COMPONENT_SWIZZLE_IDENTITY; - swapchainData->swapchainSwizzle.g = VK_COMPONENT_SWIZZLE_IDENTITY; - swapchainData->swapchainSwizzle.b = VK_COMPONENT_SWIZZLE_IDENTITY; - swapchainData->swapchainSwizzle.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - if (!VULKAN_INTERNAL_ChooseSwapSurfaceFormat( - swapchainData->swapchainFormat, - swapchainSupportDetails.formats, - swapchainSupportDetails.formatsLength, - &swapchainData->surfaceFormat - )) { - swapchainData->swapchainFormat = VK_FORMAT_B8G8R8A8_UNORM; - swapchainData->swapchainSwizzle.r = VK_COMPONENT_SWIZZLE_B; - swapchainData->swapchainSwizzle.g = VK_COMPONENT_SWIZZLE_G; - swapchainData->swapchainSwizzle.b = VK_COMPONENT_SWIZZLE_R; - swapchainData->swapchainSwizzle.a = VK_COMPONENT_SWIZZLE_A; - - if (!VULKAN_INTERNAL_ChooseSwapSurfaceFormat( - swapchainData->swapchainFormat, - swapchainSupportDetails.formats, - swapchainSupportDetails.formatsLength, - &swapchainData->surfaceFormat - )) { - renderer->vkDestroySurfaceKHR( - renderer->instance, - swapchainData->surface, - NULL - ); - if (swapchainSupportDetails.formatsLength > 0) - { - SDL_free(swapchainSupportDetails.formats); - } - if (swapchainSupportDetails.presentModesLength > 0) - { - SDL_free(swapchainSupportDetails.presentModes); - } - SDL_free(swapchainData); - Refresh_LogError("Device does not support swap chain format"); - return 0; - } - } - - if (!VULKAN_INTERNAL_ChooseSwapPresentMode( - windowData->preferredPresentMode, - swapchainSupportDetails.presentModes, - swapchainSupportDetails.presentModesLength, - &swapchainData->presentMode - )) { - renderer->vkDestroySurfaceKHR( - renderer->instance, - swapchainData->surface, - NULL - ); - if (swapchainSupportDetails.formatsLength > 0) - { - SDL_free(swapchainSupportDetails.formats); - } - if (swapchainSupportDetails.presentModesLength > 0) - { - SDL_free(swapchainSupportDetails.presentModes); - } - SDL_free(swapchainData); - Refresh_LogError("Device does not support swap chain present mode"); - return 0; - } - - SDL_Vulkan_GetDrawableSize( - (SDL_Window*) windowData->windowHandle, - &drawableWidth, - &drawableHeight - ); - - if ( drawableWidth < swapchainSupportDetails.capabilities.minImageExtent.width || - drawableWidth > swapchainSupportDetails.capabilities.maxImageExtent.width || - drawableHeight < swapchainSupportDetails.capabilities.minImageExtent.height || - drawableHeight > swapchainSupportDetails.capabilities.maxImageExtent.height ) - { - if (swapchainSupportDetails.capabilities.currentExtent.width != UINT32_MAX) - { - drawableWidth = VULKAN_INTERNAL_clamp( - drawableWidth, - swapchainSupportDetails.capabilities.minImageExtent.width, - swapchainSupportDetails.capabilities.maxImageExtent.width - ); - drawableHeight = VULKAN_INTERNAL_clamp( - drawableHeight, - swapchainSupportDetails.capabilities.minImageExtent.height, - swapchainSupportDetails.capabilities.maxImageExtent.height - ); - } - else - { - renderer->vkDestroySurfaceKHR( - renderer->instance, - swapchainData->surface, - NULL - ); - if (swapchainSupportDetails.formatsLength > 0) - { - SDL_free(swapchainSupportDetails.formats); - } - if (swapchainSupportDetails.presentModesLength > 0) - { - SDL_free(swapchainSupportDetails.presentModes); - } - SDL_free(swapchainData); - Refresh_LogError("No fallback swapchain size available!"); - return 0; - } - } - - swapchainData->extent.width = drawableWidth; - swapchainData->extent.height = drawableHeight; - - swapchainData->imageCount = swapchainSupportDetails.capabilities.minImageCount + 1; - - if ( swapchainSupportDetails.capabilities.maxImageCount > 0 && - swapchainData->imageCount > swapchainSupportDetails.capabilities.maxImageCount ) - { - swapchainData->imageCount = swapchainSupportDetails.capabilities.maxImageCount; - } - - if (swapchainData->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) - { - /* Required for proper triple-buffering. - * Note that this is below the above maxImageCount check! - * If the driver advertises MAILBOX but does not support 3 swap - * images, it's not real mailbox support, so let it fail hard. - * -flibit - */ - swapchainData->imageCount = SDL_max(swapchainData->imageCount, 3); - } - - swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - swapchainCreateInfo.pNext = NULL; - swapchainCreateInfo.flags = 0; - swapchainCreateInfo.surface = swapchainData->surface; - swapchainCreateInfo.minImageCount = swapchainData->imageCount; - swapchainCreateInfo.imageFormat = swapchainData->surfaceFormat.format; - swapchainCreateInfo.imageColorSpace = swapchainData->surfaceFormat.colorSpace; - swapchainCreateInfo.imageExtent = swapchainData->extent; - swapchainCreateInfo.imageArrayLayers = 1; - swapchainCreateInfo.imageUsage = - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT; - swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - swapchainCreateInfo.queueFamilyIndexCount = 0; - swapchainCreateInfo.pQueueFamilyIndices = NULL; - swapchainCreateInfo.preTransform = swapchainSupportDetails.capabilities.currentTransform; - swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - swapchainCreateInfo.presentMode = swapchainData->presentMode; - swapchainCreateInfo.clipped = VK_TRUE; - swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE; - - vulkanResult = renderer->vkCreateSwapchainKHR( - renderer->logicalDevice, - &swapchainCreateInfo, - NULL, - &swapchainData->swapchain - ); - - if (swapchainSupportDetails.formatsLength > 0) - { - SDL_free(swapchainSupportDetails.formats); - } - if (swapchainSupportDetails.presentModesLength > 0) - { - SDL_free(swapchainSupportDetails.presentModes); - } - - if (vulkanResult != VK_SUCCESS) - { - renderer->vkDestroySurfaceKHR( - renderer->instance, - swapchainData->surface, - NULL - ); - SDL_free(swapchainData); - LogVulkanResultAsError("vkCreateSwapchainKHR", vulkanResult); - return 0; - } - - renderer->vkGetSwapchainImagesKHR( - renderer->logicalDevice, - swapchainData->swapchain, - &swapchainData->imageCount, - NULL - ); - - swapchainData->textureContainers = SDL_malloc( - sizeof(VulkanTextureContainer) * swapchainData->imageCount - ); - - if (!swapchainData->textureContainers) - { - SDL_OutOfMemory(); - renderer->vkDestroySurfaceKHR( - renderer->instance, - swapchainData->surface, - NULL - ); - SDL_free(swapchainData); - return 0; - } - - swapchainImages = SDL_stack_alloc(VkImage, swapchainData->imageCount); - - renderer->vkGetSwapchainImagesKHR( - renderer->logicalDevice, - swapchainData->swapchain, - &swapchainData->imageCount, - swapchainImages - ); - - imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - imageViewCreateInfo.pNext = NULL; - imageViewCreateInfo.flags = 0; - imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageViewCreateInfo.format = swapchainData->surfaceFormat.format; - imageViewCreateInfo.components = swapchainData->swapchainSwizzle; - imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageViewCreateInfo.subresourceRange.baseMipLevel = 0; - imageViewCreateInfo.subresourceRange.levelCount = 1; - imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; - imageViewCreateInfo.subresourceRange.layerCount = 1; - - for (i = 0; i < swapchainData->imageCount; i += 1) - { - swapchainData->textureContainers[i].vulkanTexture = SDL_malloc(sizeof(VulkanTexture)); - - swapchainData->textureContainers[i].vulkanTexture->image = swapchainImages[i]; - - imageViewCreateInfo.image = swapchainImages[i]; - - vulkanResult = renderer->vkCreateImageView( - renderer->logicalDevice, - &imageViewCreateInfo, - NULL, - &swapchainData->textureContainers[i].vulkanTexture->view - ); - - if (vulkanResult != VK_SUCCESS) - { - renderer->vkDestroySurfaceKHR( - renderer->instance, - swapchainData->surface, - NULL - ); - SDL_stack_free(swapchainImages); - SDL_free(swapchainData->textureContainers); - SDL_free(swapchainData); - LogVulkanResultAsError("vkCreateImageView", vulkanResult); - return 0; - } - - swapchainData->textureContainers[i].vulkanTexture->resourceAccessType = RESOURCE_ACCESS_NONE; - - /* Swapchain memory is managed by the driver */ - swapchainData->textureContainers[i].vulkanTexture->usedRegion = NULL; - - swapchainData->textureContainers[i].vulkanTexture->dimensions = swapchainData->extent; - swapchainData->textureContainers[i].vulkanTexture->format = swapchainData->swapchainFormat; - swapchainData->textureContainers[i].vulkanTexture->is3D = 0; - swapchainData->textureContainers[i].vulkanTexture->isCube = 0; - swapchainData->textureContainers[i].vulkanTexture->layerCount = 1; - swapchainData->textureContainers[i].vulkanTexture->levelCount = 1; - swapchainData->textureContainers[i].vulkanTexture->sampleCount = REFRESH_SAMPLECOUNT_1; - swapchainData->textureContainers[i].vulkanTexture->usageFlags = - VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - swapchainData->textureContainers[i].vulkanTexture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT; - swapchainData->textureContainers[i].vulkanTexture->resourceAccessType = RESOURCE_ACCESS_NONE; - swapchainData->textureContainers[i].vulkanTexture->msaaTex = NULL; - } - - SDL_stack_free(swapchainImages); - - semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - semaphoreCreateInfo.pNext = NULL; - semaphoreCreateInfo.flags = 0; - - renderer->vkCreateSemaphore( - renderer->logicalDevice, - &semaphoreCreateInfo, - NULL, - &swapchainData->imageAvailableSemaphore - ); - - renderer->vkCreateSemaphore( - renderer->logicalDevice, - &semaphoreCreateInfo, - NULL, - &swapchainData->renderFinishedSemaphore - ); - - windowData->swapchainData = swapchainData; - return 1; -} - -static void VULKAN_INTERNAL_RecreateSwapchain( - VulkanRenderer* renderer, - WindowData *windowData -) { - VULKAN_Wait((Refresh_Renderer*) renderer); - VULKAN_INTERNAL_DestroySwapchain(renderer, windowData); - VULKAN_INTERNAL_CreateSwapchain(renderer, windowData); -} - -/* Command Buffers */ - -static void VULKAN_INTERNAL_BeginCommandBuffer( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer -) { - VkCommandBufferBeginInfo beginInfo; - VkResult result; - - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.pNext = NULL; - beginInfo.flags = 0; - beginInfo.pInheritanceInfo = NULL; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - - result = renderer->vkBeginCommandBuffer( - commandBuffer->commandBuffer, - &beginInfo - ); - - if (result != VK_SUCCESS) - { - LogVulkanResultAsError("vkBeginCommandBuffer", result); - } -} - -static void VULKAN_INTERNAL_EndCommandBuffer( - VulkanRenderer* renderer, - VulkanCommandBuffer *commandBuffer -) { - VkResult result; - - /* Compute pipelines are not explicitly unbound so we have to clean up here */ - if ( commandBuffer->computeUniformBuffer != renderer->dummyComputeUniformBuffer && - commandBuffer->computeUniformBuffer != NULL - ) { - VULKAN_INTERNAL_BindUniformBuffer( - renderer, - commandBuffer, - commandBuffer->computeUniformBuffer - ); - } - commandBuffer->computeUniformBuffer = NULL; - commandBuffer->currentComputePipeline = NULL; - - result = renderer->vkEndCommandBuffer( - commandBuffer->commandBuffer - ); - - if (result != VK_SUCCESS) - { - LogVulkanResultAsError("vkEndCommandBuffer", result); - } -} - -static void VULKAN_DestroyDevice( - Refresh_Device *device -) { - VulkanRenderer* renderer = (VulkanRenderer*) device->driverData; - CommandPoolHashArray commandPoolHashArray; - GraphicsPipelineLayoutHashArray graphicsPipelineLayoutHashArray; - ComputePipelineLayoutHashArray computePipelineLayoutHashArray; - VulkanMemorySubAllocator *allocator; - int32_t i, j, k; - - VULKAN_Wait(device->driverData); - - for (i = renderer->claimedWindowCount - 1; i >= 0; i -= 1) - { - VULKAN_UnclaimWindow(device->driverData, renderer->claimedWindows[i]->windowHandle); - } - - SDL_free(renderer->claimedWindows); - - VULKAN_Wait(device->driverData); - - SDL_free(renderer->submittedCommandBuffers); - - VULKAN_INTERNAL_DestroyBuffer(renderer, renderer->dummyBuffer); - - SDL_free(renderer->dummyVertexUniformBuffer); - SDL_free(renderer->dummyFragmentUniformBuffer); - SDL_free(renderer->dummyComputeUniformBuffer); - - for (i = 0; i < renderer->transferBufferPool.availableBufferCount; i += 1) - { - VULKAN_INTERNAL_DestroyBuffer(renderer, renderer->transferBufferPool.availableBuffers[i]->buffer); - SDL_free(renderer->transferBufferPool.availableBuffers[i]); - } - - SDL_free(renderer->transferBufferPool.availableBuffers); - SDL_DestroyMutex(renderer->transferBufferPool.lock); - - for (i = 0; i < renderer->fencePool.availableFenceCount; i += 1) - { - renderer->vkDestroyFence(renderer->logicalDevice, renderer->fencePool.availableFences[i], NULL); - } - - SDL_free(renderer->fencePool.availableFences); - SDL_DestroyMutex(renderer->fencePool.lock); - - for (i = 0; i < NUM_COMMAND_POOL_BUCKETS; i += 1) - { - commandPoolHashArray = renderer->commandPoolHashTable.buckets[i]; - for (j = 0; j < commandPoolHashArray.count; j += 1) - { - VULKAN_INTERNAL_DestroyCommandPool( - renderer, - commandPoolHashArray.elements[j].value - ); - } - - if (commandPoolHashArray.elements != NULL) - { - SDL_free(commandPoolHashArray.elements); - } - } - - for (i = 0; i < NUM_PIPELINE_LAYOUT_BUCKETS; i += 1) - { - graphicsPipelineLayoutHashArray = renderer->graphicsPipelineLayoutHashTable.buckets[i]; - for (j = 0; j < graphicsPipelineLayoutHashArray.count; j += 1) - { - VULKAN_INTERNAL_DestroyDescriptorSetCache( - renderer, - graphicsPipelineLayoutHashArray.elements[j].value->vertexSamplerDescriptorSetCache - ); - - VULKAN_INTERNAL_DestroyDescriptorSetCache( - renderer, - graphicsPipelineLayoutHashArray.elements[j].value->fragmentSamplerDescriptorSetCache - ); - - renderer->vkDestroyPipelineLayout( - renderer->logicalDevice, - graphicsPipelineLayoutHashArray.elements[j].value->pipelineLayout, - NULL - ); - - SDL_free(graphicsPipelineLayoutHashArray.elements[j].value); - } - - if (graphicsPipelineLayoutHashArray.elements != NULL) - { - SDL_free(graphicsPipelineLayoutHashArray.elements); - } - - computePipelineLayoutHashArray = renderer->computePipelineLayoutHashTable.buckets[i]; - for (j = 0; j < computePipelineLayoutHashArray.count; j += 1) - { - VULKAN_INTERNAL_DestroyDescriptorSetCache( - renderer, - computePipelineLayoutHashArray.elements[j].value->bufferDescriptorSetCache - ); - - VULKAN_INTERNAL_DestroyDescriptorSetCache( - renderer, - computePipelineLayoutHashArray.elements[j].value->imageDescriptorSetCache - ); - - renderer->vkDestroyPipelineLayout( - renderer->logicalDevice, - computePipelineLayoutHashArray.elements[j].value->pipelineLayout, - NULL - ); - - SDL_free(computePipelineLayoutHashArray.elements[j].value); - } - - if (computePipelineLayoutHashArray.elements != NULL) - { - SDL_free(computePipelineLayoutHashArray.elements); - } - } - - renderer->vkDestroyDescriptorPool( - renderer->logicalDevice, - renderer->defaultDescriptorPool, - NULL - ); - - for (i = 0; i < NUM_DESCRIPTOR_SET_LAYOUT_BUCKETS; i += 1) - { - for (j = 0; j < renderer->descriptorSetLayoutHashTable.buckets[i].count; j += 1) - { - renderer->vkDestroyDescriptorSetLayout( - renderer->logicalDevice, - renderer->descriptorSetLayoutHashTable.buckets[i].elements[j].value, - NULL - ); - } - - SDL_free(renderer->descriptorSetLayoutHashTable.buckets[i].elements); - } - - renderer->vkDestroyDescriptorSetLayout( - renderer->logicalDevice, - renderer->emptyVertexSamplerLayout, - NULL - ); - - renderer->vkDestroyDescriptorSetLayout( - renderer->logicalDevice, - renderer->emptyFragmentSamplerLayout, - NULL - ); - - renderer->vkDestroyDescriptorSetLayout( - renderer->logicalDevice, - renderer->emptyComputeBufferDescriptorSetLayout, - NULL - ); - - renderer->vkDestroyDescriptorSetLayout( - renderer->logicalDevice, - renderer->emptyComputeImageDescriptorSetLayout, - NULL - ); - - renderer->vkDestroyDescriptorSetLayout( - renderer->logicalDevice, - renderer->vertexUniformDescriptorSetLayout, - NULL - ); - - renderer->vkDestroyDescriptorSetLayout( - renderer->logicalDevice, - renderer->fragmentUniformDescriptorSetLayout, - NULL - ); - - renderer->vkDestroyDescriptorSetLayout( - renderer->logicalDevice, - renderer->computeUniformDescriptorSetLayout, - NULL - ); - - VULKAN_INTERNAL_DestroyUniformBufferPool(renderer, renderer->vertexUniformBufferPool); - VULKAN_INTERNAL_DestroyUniformBufferPool(renderer, renderer->fragmentUniformBufferPool); - VULKAN_INTERNAL_DestroyUniformBufferPool(renderer, renderer->computeUniformBufferPool); - - for (i = 0; i < renderer->framebufferHashArray.count; i += 1) - { - VULKAN_INTERNAL_DestroyFramebuffer( - renderer, - renderer->framebufferHashArray.elements[i].value - ); - } - - SDL_free(renderer->framebufferHashArray.elements); - - for (i = 0; i < renderer->renderPassHashArray.count; i += 1) - { - renderer->vkDestroyRenderPass( - renderer->logicalDevice, - renderer->renderPassHashArray.elements[i].value, - NULL - ); - } - - SDL_free(renderer->renderPassHashArray.elements); - - SDL_free(renderer->renderTargetHashArray.elements); - - for (i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) - { - allocator = &renderer->memoryAllocator->subAllocators[i]; - - for (j = allocator->allocationCount - 1; j >= 0; j -= 1) - { - for (k = allocator->allocations[j]->usedRegionCount - 1; k >= 0; k -= 1) - { - VULKAN_INTERNAL_RemoveMemoryUsedRegion( - renderer, - allocator->allocations[j]->usedRegions[k] - ); - } - - VULKAN_INTERNAL_DeallocateMemory( - renderer, - allocator, - j - ); - } - - if (renderer->memoryAllocator->subAllocators[i].allocations != NULL) - { - SDL_free(renderer->memoryAllocator->subAllocators[i].allocations); - } - - SDL_free(renderer->memoryAllocator->subAllocators[i].sortedFreeRegions); - } - - SDL_free(renderer->memoryAllocator); - - SDL_free(renderer->texturesToDestroy); - SDL_free(renderer->buffersToDestroy); - SDL_free(renderer->graphicsPipelinesToDestroy); - SDL_free(renderer->computePipelinesToDestroy); - SDL_free(renderer->shaderModulesToDestroy); - SDL_free(renderer->samplersToDestroy); - SDL_free(renderer->framebuffersToDestroy); - - SDL_DestroyMutex(renderer->allocatorLock); - SDL_DestroyMutex(renderer->disposeLock); - SDL_DestroyMutex(renderer->submitLock); - SDL_DestroyMutex(renderer->acquireCommandBufferLock); - SDL_DestroyMutex(renderer->renderPassFetchLock); - SDL_DestroyMutex(renderer->framebufferFetchLock); - SDL_DestroyMutex(renderer->renderTargetFetchLock); - - renderer->vkDestroyDevice(renderer->logicalDevice, NULL); - renderer->vkDestroyInstance(renderer->instance, NULL); - - SDL_free(renderer); - SDL_free(device); -} - -static void VULKAN_DrawInstancedPrimitives( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - uint32_t baseVertex, - uint32_t startIndex, - uint32_t primitiveCount, - uint32_t instanceCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - - VkDescriptorSet descriptorSets[4]; - uint32_t dynamicOffsets[2]; - - descriptorSets[0] = vulkanCommandBuffer->vertexSamplerDescriptorSet; - descriptorSets[1] = vulkanCommandBuffer->fragmentSamplerDescriptorSet; - descriptorSets[2] = vulkanCommandBuffer->vertexUniformBuffer->descriptorSet; - descriptorSets[3] = vulkanCommandBuffer->fragmentUniformBuffer->descriptorSet; - - dynamicOffsets[0] = vertexParamOffset; - dynamicOffsets[1] = fragmentParamOffset; - - renderer->vkCmdBindDescriptorSets( - vulkanCommandBuffer->commandBuffer, - VK_PIPELINE_BIND_POINT_GRAPHICS, - vulkanCommandBuffer->currentGraphicsPipeline->pipelineLayout->pipelineLayout, - 0, - 4, - descriptorSets, - 2, - dynamicOffsets - ); - - renderer->vkCmdDrawIndexed( - vulkanCommandBuffer->commandBuffer, - PrimitiveVerts( - vulkanCommandBuffer->currentGraphicsPipeline->primitiveType, - primitiveCount - ), - instanceCount, - startIndex, - baseVertex, - 0 - ); -} - -static void VULKAN_DrawIndexedPrimitives( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - uint32_t baseVertex, - uint32_t startIndex, - uint32_t primitiveCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset -) { - VULKAN_DrawInstancedPrimitives( - driverData, - commandBuffer, - baseVertex, - startIndex, - primitiveCount, - 1, - vertexParamOffset, - fragmentParamOffset - ); -} - -static void VULKAN_DrawPrimitives( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - uint32_t vertexStart, - uint32_t primitiveCount, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - - VkDescriptorSet descriptorSets[4]; - uint32_t dynamicOffsets[2]; - - descriptorSets[0] = vulkanCommandBuffer->vertexSamplerDescriptorSet; - descriptorSets[1] = vulkanCommandBuffer->fragmentSamplerDescriptorSet; - descriptorSets[2] = vulkanCommandBuffer->vertexUniformBuffer->descriptorSet; - descriptorSets[3] = vulkanCommandBuffer->fragmentUniformBuffer->descriptorSet; - - dynamicOffsets[0] = vertexParamOffset; - dynamicOffsets[1] = fragmentParamOffset; - - renderer->vkCmdBindDescriptorSets( - vulkanCommandBuffer->commandBuffer, - VK_PIPELINE_BIND_POINT_GRAPHICS, - vulkanCommandBuffer->currentGraphicsPipeline->pipelineLayout->pipelineLayout, - 0, - 4, - descriptorSets, - 2, - dynamicOffsets - ); - - renderer->vkCmdDraw( - vulkanCommandBuffer->commandBuffer, - PrimitiveVerts( - vulkanCommandBuffer->currentGraphicsPipeline->primitiveType, - primitiveCount - ), - 1, - vertexStart, - 0 - ); -} - -static void VULKAN_DrawPrimitivesIndirect( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint32_t offsetInBytes, - uint32_t drawCount, - uint32_t stride, - uint32_t vertexParamOffset, - uint32_t fragmentParamOffset -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer*) buffer)->vulkanBuffer; - VkDescriptorSet descriptorSets[4]; - uint32_t dynamicOffsets[2]; - - descriptorSets[0] = vulkanCommandBuffer->vertexSamplerDescriptorSet; - descriptorSets[1] = vulkanCommandBuffer->fragmentSamplerDescriptorSet; - descriptorSets[2] = vulkanCommandBuffer->vertexUniformBuffer->descriptorSet; - descriptorSets[3] = vulkanCommandBuffer->fragmentUniformBuffer->descriptorSet; - - dynamicOffsets[0] = vertexParamOffset; - dynamicOffsets[1] = fragmentParamOffset; - - renderer->vkCmdBindDescriptorSets( - vulkanCommandBuffer->commandBuffer, - VK_PIPELINE_BIND_POINT_GRAPHICS, - vulkanCommandBuffer->currentGraphicsPipeline->pipelineLayout->pipelineLayout, - 0, - 4, - descriptorSets, - 2, - dynamicOffsets - ); - - renderer->vkCmdDrawIndirect( - vulkanCommandBuffer->commandBuffer, - vulkanBuffer->buffer, - offsetInBytes, - drawCount, - stride - ); - - VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, vulkanBuffer); -} - -static void VULKAN_DispatchCompute( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - uint32_t groupCountX, - uint32_t groupCountY, - uint32_t groupCountZ, - uint32_t computeParamOffset -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanComputePipeline *computePipeline = vulkanCommandBuffer->currentComputePipeline; - VkDescriptorSet descriptorSets[3]; - VulkanResourceAccessType resourceAccessType = RESOURCE_ACCESS_NONE; - VulkanBuffer *currentComputeBuffer; - VulkanTexture *currentComputeTexture; - uint32_t i; - - descriptorSets[0] = vulkanCommandBuffer->bufferDescriptorSet; - descriptorSets[1] = vulkanCommandBuffer->imageDescriptorSet; - descriptorSets[2] = vulkanCommandBuffer->computeUniformBuffer->descriptorSet; - - renderer->vkCmdBindDescriptorSets( - vulkanCommandBuffer->commandBuffer, - VK_PIPELINE_BIND_POINT_COMPUTE, - computePipeline->pipelineLayout->pipelineLayout, - 0, - 3, - descriptorSets, - 1, - &computeParamOffset - ); - - renderer->vkCmdDispatch( - vulkanCommandBuffer->commandBuffer, - groupCountX, - groupCountY, - groupCountZ - ); - - /* Re-transition buffers after dispatch */ - for (i = 0; i < vulkanCommandBuffer->boundComputeBufferCount; i += 1) - { - currentComputeBuffer = vulkanCommandBuffer->boundComputeBuffers[i]; - - if (currentComputeBuffer->usage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) - { - resourceAccessType = RESOURCE_ACCESS_VERTEX_BUFFER; - } - else if (currentComputeBuffer->usage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT) - { - resourceAccessType = RESOURCE_ACCESS_INDEX_BUFFER; - } - else if (currentComputeBuffer->usage & VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT) - { - resourceAccessType = RESOURCE_ACCESS_INDIRECT_BUFFER; - } - - if (resourceAccessType != RESOURCE_ACCESS_NONE) - { - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - resourceAccessType, - currentComputeBuffer - ); - } - } - - vulkanCommandBuffer->boundComputeBufferCount = 0; - - /* Re-transition sampler images after dispatch */ - for (i = 0; i < vulkanCommandBuffer->boundComputeTextureCount; i += 1) - { - currentComputeTexture = vulkanCommandBuffer->boundComputeTextures[i]; - - if (currentComputeTexture->usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) - { - resourceAccessType = RESOURCE_ACCESS_ANY_SHADER_READ_SAMPLED_IMAGE; - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - resourceAccessType, - currentComputeTexture->aspectFlags, - 0, - currentComputeTexture->layerCount, - 0, - currentComputeTexture->levelCount, - 0, - currentComputeTexture->image, - ¤tComputeTexture->resourceAccessType - ); - } - } - - vulkanCommandBuffer->boundComputeTextureCount = 0; -} - -static VulkanTexture* VULKAN_INTERNAL_CreateTexture( - VulkanRenderer *renderer, - uint32_t width, - uint32_t height, - uint32_t depth, - uint32_t isCube, - uint32_t levelCount, - Refresh_SampleCount sampleCount, - VkFormat format, - VkImageAspectFlags aspectMask, - VkImageUsageFlags imageUsageFlags -) { - VkResult vulkanResult; - VkImageCreateInfo imageCreateInfo; - VkImageCreateFlags imageCreateFlags = 0; - VkImageViewCreateInfo imageViewCreateInfo; - uint8_t bindResult; - uint8_t is3D = depth > 1 ? 1 : 0; - uint8_t layerCount = isCube ? 6 : 1; - uint8_t isRenderTarget = - ((imageUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0) || - ((imageUsageFlags & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0); - VkComponentMapping swizzle = IDENTITY_SWIZZLE; - - VulkanTexture *texture = SDL_malloc(sizeof(VulkanTexture)); - - texture->isCube = 0; - texture->is3D = 0; - - if (isCube) - { - imageCreateFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; - texture->isCube = 1; - } - else if (is3D) - { - imageCreateFlags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; - texture->is3D = 1; - } - - imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageCreateInfo.pNext = NULL; - imageCreateInfo.flags = imageCreateFlags; - imageCreateInfo.imageType = is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.extent.width = width; - imageCreateInfo.extent.height = height; - imageCreateInfo.extent.depth = depth; - imageCreateInfo.mipLevels = levelCount; - imageCreateInfo.arrayLayers = layerCount; - imageCreateInfo.samples = RefreshToVK_SampleCount[sampleCount]; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.usage = imageUsageFlags; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCreateInfo.queueFamilyIndexCount = 0; - imageCreateInfo.pQueueFamilyIndices = NULL; - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - vulkanResult = renderer->vkCreateImage( - renderer->logicalDevice, - &imageCreateInfo, - NULL, - &texture->image - ); - VULKAN_ERROR_CHECK(vulkanResult, vkCreateImage, 0) - - bindResult = VULKAN_INTERNAL_BindMemoryForImage( - renderer, - texture->image, - isRenderTarget, - &texture->usedRegion - ); - - if (bindResult != 1) - { - renderer->vkDestroyImage( - renderer->logicalDevice, - texture->image, - NULL); - - Refresh_LogError("Unable to bind memory for texture!"); - return NULL; - } - - texture->usedRegion->vulkanTexture = texture; /* lol */ - - imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - imageViewCreateInfo.pNext = NULL; - imageViewCreateInfo.flags = 0; - imageViewCreateInfo.image = texture->image; - imageViewCreateInfo.format = format; - imageViewCreateInfo.components = swizzle; - imageViewCreateInfo.subresourceRange.aspectMask = aspectMask; - imageViewCreateInfo.subresourceRange.baseMipLevel = 0; - imageViewCreateInfo.subresourceRange.levelCount = levelCount; - imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; - imageViewCreateInfo.subresourceRange.layerCount = layerCount; - - if (isCube) - { - imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE; - } - else if (is3D) - { - imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_3D; - } - else - { - imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - } - - vulkanResult = renderer->vkCreateImageView( - renderer->logicalDevice, - &imageViewCreateInfo, - NULL, - &texture->view - ); - - if (vulkanResult != VK_SUCCESS) - { - LogVulkanResultAsError("vkCreateImageView", vulkanResult); - Refresh_LogError("Failed to create texture image view"); - return NULL; - } - - texture->dimensions.width = width; - texture->dimensions.height = height; - texture->depth = depth; - texture->format = format; - texture->levelCount = levelCount; - texture->layerCount = layerCount; - texture->sampleCount = sampleCount; - texture->resourceAccessType = RESOURCE_ACCESS_NONE; - texture->usageFlags = imageUsageFlags; - texture->aspectFlags = aspectMask; - texture->msaaTex = NULL; - - SDL_AtomicSet(&texture->referenceCount, 0); - - return texture; -} - -static VulkanRenderTarget* VULKAN_INTERNAL_CreateRenderTarget( - VulkanRenderer *renderer, - VulkanTexture *texture, - uint32_t depth, - uint32_t layer, - uint32_t level -) { - VkResult vulkanResult; - VulkanRenderTarget *renderTarget = (VulkanRenderTarget*) SDL_malloc(sizeof(VulkanRenderTarget)); - VkImageViewCreateInfo imageViewCreateInfo; - VkComponentMapping swizzle = IDENTITY_SWIZZLE; - VkImageAspectFlags aspectFlags = 0; - - if (IsDepthFormat(texture->format)) - { - aspectFlags |= VK_IMAGE_ASPECT_DEPTH_BIT; - - if (IsStencilFormat(texture->format)) - { - aspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - } - else - { - aspectFlags |= VK_IMAGE_ASPECT_COLOR_BIT; - } - - /* create framebuffer compatible views for RenderTarget */ - imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - imageViewCreateInfo.pNext = NULL; - imageViewCreateInfo.flags = 0; - imageViewCreateInfo.image = texture->image; - imageViewCreateInfo.format = texture->format; - imageViewCreateInfo.components = swizzle; - imageViewCreateInfo.subresourceRange.aspectMask = aspectFlags; - imageViewCreateInfo.subresourceRange.baseMipLevel = level; - imageViewCreateInfo.subresourceRange.levelCount = 1; - imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; - if (texture->is3D) - { - imageViewCreateInfo.subresourceRange.baseArrayLayer = depth; - } - else if (texture->isCube) - { - imageViewCreateInfo.subresourceRange.baseArrayLayer = layer; - } - imageViewCreateInfo.subresourceRange.layerCount = 1; - imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - - vulkanResult = renderer->vkCreateImageView( - renderer->logicalDevice, - &imageViewCreateInfo, - NULL, - &renderTarget->view - ); - - if (vulkanResult != VK_SUCCESS) - { - LogVulkanResultAsError( - "vkCreateImageView", - vulkanResult - ); - Refresh_LogError("Failed to create color attachment image view"); - return NULL; - } - - return renderTarget; -} - -static VulkanRenderTarget* VULKAN_INTERNAL_FetchRenderTarget( - VulkanRenderer *renderer, - VulkanTexture *texture, - uint32_t depth, - uint32_t layer, - uint32_t level -) { - RenderTargetHash hash; - VulkanRenderTarget *renderTarget; - - hash.texture = texture; - hash.depth = depth; - hash.layer = layer; - hash.level = level; - - SDL_LockMutex(renderer->renderTargetFetchLock); - - renderTarget = RenderTargetHash_Fetch( - &renderer->renderTargetHashArray, - &hash - ); - - if (renderTarget == NULL) - { - renderTarget = VULKAN_INTERNAL_CreateRenderTarget( - renderer, - texture, - depth, - layer, - level - ); - - RenderTargetHash_Insert( - &renderer->renderTargetHashArray, - hash, - renderTarget - ); - } - - SDL_UnlockMutex(renderer->renderTargetFetchLock); - - return renderTarget; -} - -static VkRenderPass VULKAN_INTERNAL_CreateRenderPass( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer, - Refresh_ColorAttachmentInfo *colorAttachmentInfos, - uint32_t colorAttachmentCount, - Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo -) { - VkResult vulkanResult; - VkAttachmentDescription attachmentDescriptions[2 * MAX_COLOR_TARGET_BINDINGS + 1]; - VkAttachmentReference colorAttachmentReferences[MAX_COLOR_TARGET_BINDINGS]; - VkAttachmentReference resolveReferences[MAX_COLOR_TARGET_BINDINGS + 1]; - VkAttachmentReference depthStencilAttachmentReference; - VkRenderPassCreateInfo renderPassCreateInfo; - VkSubpassDescription subpass; - VkRenderPass renderPass; - uint32_t i; - - uint32_t attachmentDescriptionCount = 0; - uint32_t colorAttachmentReferenceCount = 0; - uint32_t resolveReferenceCount = 0; - - VulkanTexture *texture; - VulkanTexture *msaaTexture = NULL; - - for (i = 0; i < colorAttachmentCount; i += 1) - { - texture = ((VulkanTextureContainer*) colorAttachmentInfos[i].texture)->vulkanTexture; - - if (texture->msaaTex != NULL) - { - msaaTexture = texture->msaaTex; - - /* Transition the multisample attachment */ - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - commandBuffer->commandBuffer, - RESOURCE_ACCESS_COLOR_ATTACHMENT_WRITE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - msaaTexture->layerCount, - 0, - msaaTexture->levelCount, - 0, - msaaTexture->image, - &msaaTexture->resourceAccessType - ); - - /* Resolve attachment and multisample attachment */ - - attachmentDescriptions[attachmentDescriptionCount].flags = 0; - attachmentDescriptions[attachmentDescriptionCount].format = texture->format; - attachmentDescriptions[attachmentDescriptionCount].samples = - VK_SAMPLE_COUNT_1_BIT; - attachmentDescriptions[attachmentDescriptionCount].loadOp = RefreshToVK_LoadOp[ - colorAttachmentInfos[i].loadOp - ]; - attachmentDescriptions[attachmentDescriptionCount].storeOp = - VK_ATTACHMENT_STORE_OP_STORE; /* Always store the resolve texture */ - attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = - VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = - VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].initialLayout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachmentDescriptions[attachmentDescriptionCount].finalLayout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - resolveReferences[resolveReferenceCount].attachment = - attachmentDescriptionCount; - resolveReferences[resolveReferenceCount].layout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - attachmentDescriptionCount += 1; - resolveReferenceCount += 1; - - attachmentDescriptions[attachmentDescriptionCount].flags = 0; - attachmentDescriptions[attachmentDescriptionCount].format = msaaTexture->format; - attachmentDescriptions[attachmentDescriptionCount].samples = RefreshToVK_SampleCount[ - msaaTexture->sampleCount - ]; - attachmentDescriptions[attachmentDescriptionCount].loadOp = RefreshToVK_LoadOp[ - colorAttachmentInfos[i].loadOp - ]; - attachmentDescriptions[attachmentDescriptionCount].storeOp = RefreshToVK_StoreOp[ - colorAttachmentInfos[i].storeOp - ]; - attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = - VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = - VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].initialLayout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachmentDescriptions[attachmentDescriptionCount].finalLayout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - colorAttachmentReferences[colorAttachmentReferenceCount].attachment = - attachmentDescriptionCount; - colorAttachmentReferences[colorAttachmentReferenceCount].layout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - attachmentDescriptionCount += 1; - colorAttachmentReferenceCount += 1; - } - else - { - attachmentDescriptions[attachmentDescriptionCount].flags = 0; - attachmentDescriptions[attachmentDescriptionCount].format = texture->format; - attachmentDescriptions[attachmentDescriptionCount].samples = - VK_SAMPLE_COUNT_1_BIT; - attachmentDescriptions[attachmentDescriptionCount].loadOp = RefreshToVK_LoadOp[ - colorAttachmentInfos[i].loadOp - ]; - attachmentDescriptions[attachmentDescriptionCount].storeOp = - VK_ATTACHMENT_STORE_OP_STORE; /* Always store non-MSAA textures */ - attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = - VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = - VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].initialLayout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachmentDescriptions[attachmentDescriptionCount].finalLayout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - - colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount; - colorAttachmentReferences[colorAttachmentReferenceCount].layout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - attachmentDescriptionCount += 1; - colorAttachmentReferenceCount += 1; - } - } - - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.flags = 0; - subpass.inputAttachmentCount = 0; - subpass.pInputAttachments = NULL; - subpass.colorAttachmentCount = colorAttachmentCount; - subpass.pColorAttachments = colorAttachmentReferences; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = NULL; - - if (depthStencilAttachmentInfo == NULL) - { - subpass.pDepthStencilAttachment = NULL; - } - else - { - texture = ((VulkanTextureContainer*) depthStencilAttachmentInfo->texture)->vulkanTexture; - - attachmentDescriptions[attachmentDescriptionCount].flags = 0; - attachmentDescriptions[attachmentDescriptionCount].format = texture->format; - attachmentDescriptions[attachmentDescriptionCount].samples = RefreshToVK_SampleCount[ - texture->sampleCount - ]; - attachmentDescriptions[attachmentDescriptionCount].loadOp = RefreshToVK_LoadOp[ - depthStencilAttachmentInfo->loadOp - ]; - attachmentDescriptions[attachmentDescriptionCount].storeOp = RefreshToVK_StoreOp[ - depthStencilAttachmentInfo->storeOp - ]; - attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = RefreshToVK_LoadOp[ - depthStencilAttachmentInfo->stencilLoadOp - ]; - attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = RefreshToVK_StoreOp[ - depthStencilAttachmentInfo->stencilStoreOp - ]; - attachmentDescriptions[attachmentDescriptionCount].initialLayout = - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachmentDescriptions[attachmentDescriptionCount].finalLayout = - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - depthStencilAttachmentReference.attachment = - attachmentDescriptionCount; - depthStencilAttachmentReference.layout = - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - subpass.pDepthStencilAttachment = - &depthStencilAttachmentReference; - - attachmentDescriptionCount += 1; - } - - if (msaaTexture != NULL) - { - subpass.pResolveAttachments = resolveReferences; - } - else - { - subpass.pResolveAttachments = NULL; - } - - renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCreateInfo.pNext = NULL; - renderPassCreateInfo.flags = 0; - renderPassCreateInfo.pAttachments = attachmentDescriptions; - renderPassCreateInfo.attachmentCount = attachmentDescriptionCount; - renderPassCreateInfo.subpassCount = 1; - renderPassCreateInfo.pSubpasses = &subpass; - renderPassCreateInfo.dependencyCount = 0; - renderPassCreateInfo.pDependencies = NULL; - - vulkanResult = renderer->vkCreateRenderPass( - renderer->logicalDevice, - &renderPassCreateInfo, - NULL, - &renderPass - ); - - if (vulkanResult != VK_SUCCESS) - { - renderPass = VK_NULL_HANDLE; - LogVulkanResultAsError("vkCreateRenderPass", vulkanResult); - } - - return renderPass; -} - -static VkRenderPass VULKAN_INTERNAL_CreateTransientRenderPass( - VulkanRenderer *renderer, - Refresh_GraphicsPipelineAttachmentInfo attachmentInfo, - Refresh_SampleCount sampleCount -) { - VkAttachmentDescription attachmentDescriptions[2 * MAX_COLOR_TARGET_BINDINGS + 1]; - VkAttachmentReference colorAttachmentReferences[MAX_COLOR_TARGET_BINDINGS]; - VkAttachmentReference resolveReferences[MAX_COLOR_TARGET_BINDINGS + 1]; - VkAttachmentReference depthStencilAttachmentReference; - Refresh_ColorAttachmentDescription attachmentDescription; - VkSubpassDescription subpass; - VkRenderPassCreateInfo renderPassCreateInfo; - VkRenderPass renderPass; - VkResult result; - - uint32_t multisampling = 0; - uint32_t attachmentDescriptionCount = 0; - uint32_t colorAttachmentReferenceCount = 0; - uint32_t resolveReferenceCount = 0; - uint32_t i; - - for (i = 0; i < attachmentInfo.colorAttachmentCount; i += 1) - { - attachmentDescription = attachmentInfo.colorAttachmentDescriptions[i]; - - if (sampleCount > REFRESH_SAMPLECOUNT_1) - { - multisampling = 1; - - /* Resolve attachment and multisample attachment */ - - attachmentDescriptions[attachmentDescriptionCount].flags = 0; - attachmentDescriptions[attachmentDescriptionCount].format = RefreshToVK_SurfaceFormat[ - attachmentDescription.format - ]; - attachmentDescriptions[attachmentDescriptionCount].samples = VK_SAMPLE_COUNT_1_BIT; - attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - resolveReferences[resolveReferenceCount].attachment = attachmentDescriptionCount; - resolveReferences[resolveReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - attachmentDescriptionCount += 1; - resolveReferenceCount += 1; - - attachmentDescriptions[attachmentDescriptionCount].flags = 0; - attachmentDescriptions[attachmentDescriptionCount].format = RefreshToVK_SurfaceFormat[ - attachmentDescription.format - ]; - attachmentDescriptions[attachmentDescriptionCount].samples = RefreshToVK_SampleCount[ - sampleCount - ]; - attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - colorAttachmentReferences[colorAttachmentReferenceCount].attachment = - attachmentDescriptionCount; - colorAttachmentReferences[colorAttachmentReferenceCount].layout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - attachmentDescriptionCount += 1; - colorAttachmentReferenceCount += 1; - } - else - { - attachmentDescriptions[attachmentDescriptionCount].flags = 0; - attachmentDescriptions[attachmentDescriptionCount].format = RefreshToVK_SurfaceFormat[ - attachmentDescription.format - ]; - attachmentDescriptions[attachmentDescriptionCount].samples = - VK_SAMPLE_COUNT_1_BIT; - attachmentDescriptions[attachmentDescriptionCount].loadOp = - VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].storeOp = - VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = - VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = - VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].initialLayout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - attachmentDescriptions[attachmentDescriptionCount].finalLayout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - - colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount; - colorAttachmentReferences[colorAttachmentReferenceCount].layout = - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - attachmentDescriptionCount += 1; - colorAttachmentReferenceCount += 1; - } - } - - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.flags = 0; - subpass.inputAttachmentCount = 0; - subpass.pInputAttachments = NULL; - subpass.colorAttachmentCount = attachmentInfo.colorAttachmentCount; - subpass.pColorAttachments = colorAttachmentReferences; - subpass.preserveAttachmentCount = 0; - subpass.pPreserveAttachments = NULL; - - if (attachmentInfo.hasDepthStencilAttachment) - { - attachmentDescriptions[attachmentDescriptionCount].flags = 0; - attachmentDescriptions[attachmentDescriptionCount].format = RefreshToVK_DepthFormat( - renderer, - attachmentInfo.depthStencilFormat - ); - attachmentDescriptions[attachmentDescriptionCount].samples = RefreshToVK_SampleCount[ - sampleCount - ]; - attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachmentDescriptions[attachmentDescriptionCount].initialLayout = - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - attachmentDescriptions[attachmentDescriptionCount].finalLayout = - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - depthStencilAttachmentReference.attachment = - attachmentDescriptionCount; - depthStencilAttachmentReference.layout = - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - - subpass.pDepthStencilAttachment = - &depthStencilAttachmentReference; - - attachmentDescriptionCount += 1; - } - else - { - subpass.pDepthStencilAttachment = NULL; - } - - if (multisampling) - { - subpass.pResolveAttachments = resolveReferences; - } - else - { - subpass.pResolveAttachments = NULL; - } - - renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCreateInfo.pNext = NULL; - renderPassCreateInfo.flags = 0; - renderPassCreateInfo.pAttachments = attachmentDescriptions; - renderPassCreateInfo.attachmentCount = attachmentDescriptionCount; - renderPassCreateInfo.subpassCount = 1; - renderPassCreateInfo.pSubpasses = &subpass; - renderPassCreateInfo.dependencyCount = 0; - renderPassCreateInfo.pDependencies = NULL; - - result = renderer->vkCreateRenderPass( - renderer->logicalDevice, - &renderPassCreateInfo, - NULL, - &renderPass - ); - - if (result != VK_SUCCESS) - { - renderPass = VK_NULL_HANDLE; - LogVulkanResultAsError("vkCreateRenderPass", result); - } - - return renderPass; -} - -static Refresh_GraphicsPipeline* VULKAN_CreateGraphicsPipeline( - Refresh_Renderer *driverData, - Refresh_GraphicsPipelineCreateInfo *pipelineCreateInfo -) { - VkResult vulkanResult; - uint32_t i; - Refresh_SampleCount actualSampleCount; - - VulkanGraphicsPipeline *graphicsPipeline = (VulkanGraphicsPipeline*) SDL_malloc(sizeof(VulkanGraphicsPipeline)); - VkGraphicsPipelineCreateInfo vkPipelineCreateInfo; - - VkPipelineShaderStageCreateInfo shaderStageCreateInfos[2]; - - VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo; - VkVertexInputBindingDescription *vertexInputBindingDescriptions = SDL_stack_alloc(VkVertexInputBindingDescription, pipelineCreateInfo->vertexInputState.vertexBindingCount); - VkVertexInputAttributeDescription *vertexInputAttributeDescriptions = SDL_stack_alloc(VkVertexInputAttributeDescription, pipelineCreateInfo->vertexInputState.vertexAttributeCount); - - VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo; - - VkPipelineViewportStateCreateInfo viewportStateCreateInfo; - - VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo; - - VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo; - - VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo; - VkStencilOpState frontStencilState; - VkStencilOpState backStencilState; - - VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo; - VkPipelineColorBlendAttachmentState *colorBlendAttachmentStates = SDL_stack_alloc( - VkPipelineColorBlendAttachmentState, - pipelineCreateInfo->attachmentInfo.colorAttachmentCount - ); - - static const VkDynamicState dynamicStates[] = - { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR - }; - VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo; - - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - - /* Find a compatible sample count to use */ - - actualSampleCount = VULKAN_INTERNAL_GetMaxMultiSampleCount( - renderer, - pipelineCreateInfo->multisampleState.multisampleCount - ); - - /* Create a "compatible" render pass */ - - VkRenderPass transientRenderPass = VULKAN_INTERNAL_CreateTransientRenderPass( - renderer, - pipelineCreateInfo->attachmentInfo, - actualSampleCount - ); - - /* Dynamic state */ - - dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicStateCreateInfo.pNext = NULL; - dynamicStateCreateInfo.flags = 0; - dynamicStateCreateInfo.dynamicStateCount = SDL_arraysize(dynamicStates); - dynamicStateCreateInfo.pDynamicStates = dynamicStates; - - /* Shader stages */ - - graphicsPipeline->vertexShaderModule = (VulkanShaderModule*) pipelineCreateInfo->vertexShaderInfo.shaderModule; - SDL_AtomicIncRef(&graphicsPipeline->vertexShaderModule->referenceCount); - - shaderStageCreateInfos[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStageCreateInfos[0].pNext = NULL; - shaderStageCreateInfos[0].flags = 0; - shaderStageCreateInfos[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - shaderStageCreateInfos[0].module = graphicsPipeline->vertexShaderModule->shaderModule; - shaderStageCreateInfos[0].pName = pipelineCreateInfo->vertexShaderInfo.entryPointName; - shaderStageCreateInfos[0].pSpecializationInfo = NULL; - - graphicsPipeline->vertexUniformBlockSize = - VULKAN_INTERNAL_NextHighestAlignment( - pipelineCreateInfo->vertexShaderInfo.uniformBufferSize, - renderer->minUBOAlignment - ); - - graphicsPipeline->fragmentShaderModule = (VulkanShaderModule*) pipelineCreateInfo->fragmentShaderInfo.shaderModule; - SDL_AtomicIncRef(&graphicsPipeline->fragmentShaderModule->referenceCount); - - shaderStageCreateInfos[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStageCreateInfos[1].pNext = NULL; - shaderStageCreateInfos[1].flags = 0; - shaderStageCreateInfos[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - shaderStageCreateInfos[1].module = graphicsPipeline->fragmentShaderModule->shaderModule; - shaderStageCreateInfos[1].pName = pipelineCreateInfo->fragmentShaderInfo.entryPointName; - shaderStageCreateInfos[1].pSpecializationInfo = NULL; - - graphicsPipeline->fragmentUniformBlockSize = - VULKAN_INTERNAL_NextHighestAlignment( - pipelineCreateInfo->fragmentShaderInfo.uniformBufferSize, - renderer->minUBOAlignment - ); - - /* Vertex input */ - - for (i = 0; i < pipelineCreateInfo->vertexInputState.vertexBindingCount; i += 1) - { - vertexInputBindingDescriptions[i].binding = pipelineCreateInfo->vertexInputState.vertexBindings[i].binding; - vertexInputBindingDescriptions[i].inputRate = RefreshToVK_VertexInputRate[ - pipelineCreateInfo->vertexInputState.vertexBindings[i].inputRate - ]; - vertexInputBindingDescriptions[i].stride = pipelineCreateInfo->vertexInputState.vertexBindings[i].stride; - } - - for (i = 0; i < pipelineCreateInfo->vertexInputState.vertexAttributeCount; i += 1) - { - vertexInputAttributeDescriptions[i].binding = pipelineCreateInfo->vertexInputState.vertexAttributes[i].binding; - vertexInputAttributeDescriptions[i].format = RefreshToVK_VertexFormat[ - pipelineCreateInfo->vertexInputState.vertexAttributes[i].format - ]; - vertexInputAttributeDescriptions[i].location = pipelineCreateInfo->vertexInputState.vertexAttributes[i].location; - vertexInputAttributeDescriptions[i].offset = pipelineCreateInfo->vertexInputState.vertexAttributes[i].offset; - } - - vertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputStateCreateInfo.pNext = NULL; - vertexInputStateCreateInfo.flags = 0; - vertexInputStateCreateInfo.vertexBindingDescriptionCount = pipelineCreateInfo->vertexInputState.vertexBindingCount; - vertexInputStateCreateInfo.pVertexBindingDescriptions = vertexInputBindingDescriptions; - vertexInputStateCreateInfo.vertexAttributeDescriptionCount = pipelineCreateInfo->vertexInputState.vertexAttributeCount; - vertexInputStateCreateInfo.pVertexAttributeDescriptions = vertexInputAttributeDescriptions; - - /* Topology */ - - inputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssemblyStateCreateInfo.pNext = NULL; - inputAssemblyStateCreateInfo.flags = 0; - inputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE; - inputAssemblyStateCreateInfo.topology = RefreshToVK_PrimitiveType[ - pipelineCreateInfo->primitiveType - ]; - - graphicsPipeline->primitiveType = pipelineCreateInfo->primitiveType; - - /* Viewport */ - - /* NOTE: viewport and scissor are dynamic, and must be set using the command buffer */ - - viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportStateCreateInfo.pNext = NULL; - viewportStateCreateInfo.flags = 0; - viewportStateCreateInfo.viewportCount = 1; - viewportStateCreateInfo.pViewports = NULL; - viewportStateCreateInfo.scissorCount = 1; - viewportStateCreateInfo.pScissors = NULL; - - /* Rasterization */ - - rasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizationStateCreateInfo.pNext = NULL; - rasterizationStateCreateInfo.flags = 0; - rasterizationStateCreateInfo.depthClampEnable = VK_FALSE; - rasterizationStateCreateInfo.rasterizerDiscardEnable = VK_FALSE; - rasterizationStateCreateInfo.polygonMode = RefreshToVK_PolygonMode[ - pipelineCreateInfo->rasterizerState.fillMode - ]; - rasterizationStateCreateInfo.cullMode = RefreshToVK_CullMode[ - pipelineCreateInfo->rasterizerState.cullMode - ]; - rasterizationStateCreateInfo.frontFace = RefreshToVK_FrontFace[ - pipelineCreateInfo->rasterizerState.frontFace - ]; - rasterizationStateCreateInfo.depthBiasEnable = - pipelineCreateInfo->rasterizerState.depthBiasEnable; - rasterizationStateCreateInfo.depthBiasConstantFactor = - pipelineCreateInfo->rasterizerState.depthBiasConstantFactor; - rasterizationStateCreateInfo.depthBiasClamp = - pipelineCreateInfo->rasterizerState.depthBiasClamp; - rasterizationStateCreateInfo.depthBiasSlopeFactor = - pipelineCreateInfo->rasterizerState.depthBiasSlopeFactor; - rasterizationStateCreateInfo.lineWidth = 1.0f; - - /* Multisample */ - - multisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampleStateCreateInfo.pNext = NULL; - multisampleStateCreateInfo.flags = 0; - multisampleStateCreateInfo.rasterizationSamples = RefreshToVK_SampleCount[actualSampleCount]; - multisampleStateCreateInfo.sampleShadingEnable = VK_FALSE; - multisampleStateCreateInfo.minSampleShading = 1.0f; - multisampleStateCreateInfo.pSampleMask = - &pipelineCreateInfo->multisampleState.sampleMask; - multisampleStateCreateInfo.alphaToCoverageEnable = VK_FALSE; - multisampleStateCreateInfo.alphaToOneEnable = VK_FALSE; - - /* Depth Stencil State */ - - frontStencilState.failOp = RefreshToVK_StencilOp[ - pipelineCreateInfo->depthStencilState.frontStencilState.failOp - ]; - frontStencilState.passOp = RefreshToVK_StencilOp[ - pipelineCreateInfo->depthStencilState.frontStencilState.passOp - ]; - frontStencilState.depthFailOp = RefreshToVK_StencilOp[ - pipelineCreateInfo->depthStencilState.frontStencilState.depthFailOp - ]; - frontStencilState.compareOp = RefreshToVK_CompareOp[ - pipelineCreateInfo->depthStencilState.frontStencilState.compareOp - ]; - frontStencilState.compareMask = - pipelineCreateInfo->depthStencilState.frontStencilState.compareMask; - frontStencilState.writeMask = - pipelineCreateInfo->depthStencilState.frontStencilState.writeMask; - frontStencilState.reference = - pipelineCreateInfo->depthStencilState.frontStencilState.reference; - - backStencilState.failOp = RefreshToVK_StencilOp[ - pipelineCreateInfo->depthStencilState.backStencilState.failOp - ]; - backStencilState.passOp = RefreshToVK_StencilOp[ - pipelineCreateInfo->depthStencilState.backStencilState.passOp - ]; - backStencilState.depthFailOp = RefreshToVK_StencilOp[ - pipelineCreateInfo->depthStencilState.backStencilState.depthFailOp - ]; - backStencilState.compareOp = RefreshToVK_CompareOp[ - pipelineCreateInfo->depthStencilState.backStencilState.compareOp - ]; - backStencilState.compareMask = - pipelineCreateInfo->depthStencilState.backStencilState.compareMask; - backStencilState.writeMask = - pipelineCreateInfo->depthStencilState.backStencilState.writeMask; - backStencilState.reference = - pipelineCreateInfo->depthStencilState.backStencilState.reference; - - - depthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - depthStencilStateCreateInfo.pNext = NULL; - depthStencilStateCreateInfo.flags = 0; - depthStencilStateCreateInfo.depthTestEnable = - pipelineCreateInfo->depthStencilState.depthTestEnable; - depthStencilStateCreateInfo.depthWriteEnable = - pipelineCreateInfo->depthStencilState.depthWriteEnable; - depthStencilStateCreateInfo.depthCompareOp = RefreshToVK_CompareOp[ - pipelineCreateInfo->depthStencilState.compareOp - ]; - depthStencilStateCreateInfo.depthBoundsTestEnable = - pipelineCreateInfo->depthStencilState.depthBoundsTestEnable; - depthStencilStateCreateInfo.stencilTestEnable = - pipelineCreateInfo->depthStencilState.stencilTestEnable; - depthStencilStateCreateInfo.front = frontStencilState; - depthStencilStateCreateInfo.back = backStencilState; - depthStencilStateCreateInfo.minDepthBounds = - pipelineCreateInfo->depthStencilState.minDepthBounds; - depthStencilStateCreateInfo.maxDepthBounds = - pipelineCreateInfo->depthStencilState.maxDepthBounds; - - /* Color Blend */ - - for (i = 0; i < pipelineCreateInfo->attachmentInfo.colorAttachmentCount; i += 1) - { - Refresh_ColorAttachmentBlendState blendState = pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions[i].blendState; - - colorBlendAttachmentStates[i].blendEnable = - blendState.blendEnable; - colorBlendAttachmentStates[i].srcColorBlendFactor = RefreshToVK_BlendFactor[ - blendState.srcColorBlendFactor - ]; - colorBlendAttachmentStates[i].dstColorBlendFactor = RefreshToVK_BlendFactor[ - blendState.dstColorBlendFactor - ]; - colorBlendAttachmentStates[i].colorBlendOp = RefreshToVK_BlendOp[ - blendState.colorBlendOp - ]; - colorBlendAttachmentStates[i].srcAlphaBlendFactor = RefreshToVK_BlendFactor[ - blendState.srcAlphaBlendFactor - ]; - colorBlendAttachmentStates[i].dstAlphaBlendFactor = RefreshToVK_BlendFactor[ - blendState.dstAlphaBlendFactor - ]; - colorBlendAttachmentStates[i].alphaBlendOp = RefreshToVK_BlendOp[ - blendState.alphaBlendOp - ]; - colorBlendAttachmentStates[i].colorWriteMask = - blendState.colorWriteMask; - } - - colorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlendStateCreateInfo.pNext = NULL; - colorBlendStateCreateInfo.flags = 0; - colorBlendStateCreateInfo.attachmentCount = - pipelineCreateInfo->attachmentInfo.colorAttachmentCount; - colorBlendStateCreateInfo.pAttachments = - colorBlendAttachmentStates; - colorBlendStateCreateInfo.blendConstants[0] = - pipelineCreateInfo->blendConstants[0]; - colorBlendStateCreateInfo.blendConstants[1] = - pipelineCreateInfo->blendConstants[1]; - colorBlendStateCreateInfo.blendConstants[2] = - pipelineCreateInfo->blendConstants[2]; - colorBlendStateCreateInfo.blendConstants[3] = - pipelineCreateInfo->blendConstants[3]; - - /* We don't support LogicOp, so this is easy. */ - colorBlendStateCreateInfo.logicOpEnable = VK_FALSE; - colorBlendStateCreateInfo.logicOp = 0; - - /* Pipeline Layout */ - - graphicsPipeline->pipelineLayout = VULKAN_INTERNAL_FetchGraphicsPipelineLayout( - renderer, - pipelineCreateInfo->vertexShaderInfo.samplerBindingCount, - pipelineCreateInfo->fragmentShaderInfo.samplerBindingCount - ); - - /* Pipeline */ - - vkPipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - vkPipelineCreateInfo.pNext = NULL; - vkPipelineCreateInfo.flags = 0; - vkPipelineCreateInfo.stageCount = 2; - vkPipelineCreateInfo.pStages = shaderStageCreateInfos; - vkPipelineCreateInfo.pVertexInputState = &vertexInputStateCreateInfo; - vkPipelineCreateInfo.pInputAssemblyState = &inputAssemblyStateCreateInfo; - vkPipelineCreateInfo.pTessellationState = VK_NULL_HANDLE; - vkPipelineCreateInfo.pViewportState = &viewportStateCreateInfo; - vkPipelineCreateInfo.pRasterizationState = &rasterizationStateCreateInfo; - vkPipelineCreateInfo.pMultisampleState = &multisampleStateCreateInfo; - vkPipelineCreateInfo.pDepthStencilState = &depthStencilStateCreateInfo; - vkPipelineCreateInfo.pColorBlendState = &colorBlendStateCreateInfo; - vkPipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo; - vkPipelineCreateInfo.layout = graphicsPipeline->pipelineLayout->pipelineLayout; - vkPipelineCreateInfo.renderPass = transientRenderPass; - vkPipelineCreateInfo.subpass = 0; - vkPipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE; - vkPipelineCreateInfo.basePipelineIndex = 0; - - /* TODO: enable pipeline caching */ - vulkanResult = renderer->vkCreateGraphicsPipelines( - renderer->logicalDevice, - VK_NULL_HANDLE, - 1, - &vkPipelineCreateInfo, - NULL, - &graphicsPipeline->pipeline - ); - - SDL_stack_free(vertexInputBindingDescriptions); - SDL_stack_free(vertexInputAttributeDescriptions); - SDL_stack_free(colorBlendAttachmentStates); - - renderer->vkDestroyRenderPass( - renderer->logicalDevice, - transientRenderPass, - NULL - ); - - if (vulkanResult != VK_SUCCESS) - { - SDL_free(graphicsPipeline); - LogVulkanResultAsError("vkCreateGraphicsPipelines", vulkanResult); - Refresh_LogError("Failed to create graphics pipeline!"); - return NULL; - } - - SDL_AtomicSet(&graphicsPipeline->referenceCount, 0); - - return (Refresh_GraphicsPipeline*) graphicsPipeline; -} - -static VulkanComputePipelineLayout* VULKAN_INTERNAL_FetchComputePipelineLayout( - VulkanRenderer *renderer, - uint32_t bufferBindingCount, - uint32_t imageBindingCount -) { - VkResult vulkanResult; - VkDescriptorSetLayout setLayouts[3]; - VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo; - ComputePipelineLayoutHash pipelineLayoutHash; - VulkanComputePipelineLayout *vulkanComputePipelineLayout; - - pipelineLayoutHash.bufferLayout = VULKAN_INTERNAL_FetchDescriptorSetLayout( - renderer, - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - bufferBindingCount, - VK_SHADER_STAGE_COMPUTE_BIT - ); - - pipelineLayoutHash.imageLayout = VULKAN_INTERNAL_FetchDescriptorSetLayout( - renderer, - VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - imageBindingCount, - VK_SHADER_STAGE_COMPUTE_BIT - ); - - pipelineLayoutHash.uniformLayout = renderer->computeUniformDescriptorSetLayout; - - vulkanComputePipelineLayout = ComputePipelineLayoutHashArray_Fetch( - &renderer->computePipelineLayoutHashTable, - pipelineLayoutHash - ); - - if (vulkanComputePipelineLayout != NULL) - { - return vulkanComputePipelineLayout; - } - - vulkanComputePipelineLayout = SDL_malloc(sizeof(VulkanComputePipelineLayout)); - - setLayouts[0] = pipelineLayoutHash.bufferLayout; - setLayouts[1] = pipelineLayoutHash.imageLayout; - setLayouts[2] = pipelineLayoutHash.uniformLayout; - - pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutCreateInfo.pNext = NULL; - pipelineLayoutCreateInfo.flags = 0; - pipelineLayoutCreateInfo.setLayoutCount = 3; - pipelineLayoutCreateInfo.pSetLayouts = setLayouts; - pipelineLayoutCreateInfo.pushConstantRangeCount = 0; - pipelineLayoutCreateInfo.pPushConstantRanges = NULL; - - vulkanResult = renderer->vkCreatePipelineLayout( - renderer->logicalDevice, - &pipelineLayoutCreateInfo, - NULL, - &vulkanComputePipelineLayout->pipelineLayout - ); - - if (vulkanResult != VK_SUCCESS) - { - LogVulkanResultAsError("vkCreatePipelineLayout", vulkanResult); - return NULL; - } - - ComputePipelineLayoutHashArray_Insert( - &renderer->computePipelineLayoutHashTable, - pipelineLayoutHash, - vulkanComputePipelineLayout - ); - - /* If the binding count is 0 - * we can just bind the same descriptor set - * so no cache is needed - */ - - if (bufferBindingCount == 0) - { - vulkanComputePipelineLayout->bufferDescriptorSetCache = NULL; - } - else - { - vulkanComputePipelineLayout->bufferDescriptorSetCache = - VULKAN_INTERNAL_CreateDescriptorSetCache( - renderer, - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - pipelineLayoutHash.bufferLayout, - bufferBindingCount - ); - } - - if (imageBindingCount == 0) - { - vulkanComputePipelineLayout->imageDescriptorSetCache = NULL; - } - else - { - vulkanComputePipelineLayout->imageDescriptorSetCache = - VULKAN_INTERNAL_CreateDescriptorSetCache( - renderer, - VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - pipelineLayoutHash.imageLayout, - imageBindingCount - ); - } - - return vulkanComputePipelineLayout; -} - -static Refresh_ComputePipeline* VULKAN_CreateComputePipeline( - Refresh_Renderer *driverData, - Refresh_ComputeShaderInfo *computeShaderInfo -) { - VkComputePipelineCreateInfo computePipelineCreateInfo; - VkPipelineShaderStageCreateInfo pipelineShaderStageCreateInfo; - - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanComputePipeline *vulkanComputePipeline = SDL_malloc(sizeof(VulkanComputePipeline)); - - vulkanComputePipeline->computeShaderModule = (VulkanShaderModule*) computeShaderInfo->shaderModule; - SDL_AtomicIncRef(&vulkanComputePipeline->computeShaderModule->referenceCount); - - pipelineShaderStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - pipelineShaderStageCreateInfo.pNext = NULL; - pipelineShaderStageCreateInfo.flags = 0; - pipelineShaderStageCreateInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; - pipelineShaderStageCreateInfo.module = vulkanComputePipeline->computeShaderModule->shaderModule; - pipelineShaderStageCreateInfo.pName = computeShaderInfo->entryPointName; - pipelineShaderStageCreateInfo.pSpecializationInfo = NULL; - - vulkanComputePipeline->pipelineLayout = VULKAN_INTERNAL_FetchComputePipelineLayout( - renderer, - computeShaderInfo->bufferBindingCount, - computeShaderInfo->imageBindingCount - ); - - computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; - computePipelineCreateInfo.pNext = NULL; - computePipelineCreateInfo.flags = 0; - computePipelineCreateInfo.stage = pipelineShaderStageCreateInfo; - computePipelineCreateInfo.layout = - vulkanComputePipeline->pipelineLayout->pipelineLayout; - computePipelineCreateInfo.basePipelineHandle = NULL; - computePipelineCreateInfo.basePipelineIndex = 0; - - renderer->vkCreateComputePipelines( - renderer->logicalDevice, - NULL, - 1, - &computePipelineCreateInfo, - NULL, - &vulkanComputePipeline->pipeline - ); - - vulkanComputePipeline->uniformBlockSize = - VULKAN_INTERNAL_NextHighestAlignment( - computeShaderInfo->uniformBufferSize, - renderer->minUBOAlignment - ); - - SDL_AtomicSet(&vulkanComputePipeline->referenceCount, 0); - - return (Refresh_ComputePipeline*) vulkanComputePipeline; -} - -static Refresh_Sampler* VULKAN_CreateSampler( - Refresh_Renderer *driverData, - Refresh_SamplerStateCreateInfo *samplerStateCreateInfo -) { - VulkanRenderer* renderer = (VulkanRenderer*)driverData; - VulkanSampler *vulkanSampler = SDL_malloc(sizeof(VulkanSampler)); - VkResult vulkanResult; - - VkSamplerCreateInfo vkSamplerCreateInfo; - vkSamplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - vkSamplerCreateInfo.pNext = NULL; - vkSamplerCreateInfo.flags = 0; - vkSamplerCreateInfo.magFilter = RefreshToVK_Filter[ - samplerStateCreateInfo->magFilter - ]; - vkSamplerCreateInfo.minFilter = RefreshToVK_Filter[ - samplerStateCreateInfo->minFilter - ]; - vkSamplerCreateInfo.mipmapMode = RefreshToVK_SamplerMipmapMode[ - samplerStateCreateInfo->mipmapMode - ]; - vkSamplerCreateInfo.addressModeU = RefreshToVK_SamplerAddressMode[ - samplerStateCreateInfo->addressModeU - ]; - vkSamplerCreateInfo.addressModeV = RefreshToVK_SamplerAddressMode[ - samplerStateCreateInfo->addressModeV - ]; - vkSamplerCreateInfo.addressModeW = RefreshToVK_SamplerAddressMode[ - samplerStateCreateInfo->addressModeW - ]; - vkSamplerCreateInfo.mipLodBias = samplerStateCreateInfo->mipLodBias; - vkSamplerCreateInfo.anisotropyEnable = samplerStateCreateInfo->anisotropyEnable; - vkSamplerCreateInfo.maxAnisotropy = samplerStateCreateInfo->maxAnisotropy; - vkSamplerCreateInfo.compareEnable = samplerStateCreateInfo->compareEnable; - vkSamplerCreateInfo.compareOp = RefreshToVK_CompareOp[ - samplerStateCreateInfo->compareOp - ]; - vkSamplerCreateInfo.minLod = samplerStateCreateInfo->minLod; - vkSamplerCreateInfo.maxLod = samplerStateCreateInfo->maxLod; - vkSamplerCreateInfo.borderColor = RefreshToVK_BorderColor[ - samplerStateCreateInfo->borderColor - ]; - vkSamplerCreateInfo.unnormalizedCoordinates = VK_FALSE; - - vulkanResult = renderer->vkCreateSampler( - renderer->logicalDevice, - &vkSamplerCreateInfo, - NULL, - &vulkanSampler->sampler - ); - - if (vulkanResult != VK_SUCCESS) - { - SDL_free(vulkanSampler); - LogVulkanResultAsError("vkCreateSampler", vulkanResult); - return NULL; - } - - SDL_AtomicSet(&vulkanSampler->referenceCount, 0); - - return (Refresh_Sampler*) vulkanSampler; -} - -static Refresh_ShaderModule* VULKAN_CreateShaderModule( - Refresh_Renderer *driverData, - Refresh_ShaderModuleCreateInfo *shaderModuleCreateInfo -) { - VulkanShaderModule *vulkanShaderModule = SDL_malloc(sizeof(VulkanShaderModule)); - VkResult vulkanResult; - VkShaderModuleCreateInfo vkShaderModuleCreateInfo; - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - - vkShaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - vkShaderModuleCreateInfo.pNext = NULL; - vkShaderModuleCreateInfo.flags = 0; - vkShaderModuleCreateInfo.codeSize = shaderModuleCreateInfo->codeSize; - vkShaderModuleCreateInfo.pCode = (uint32_t*) shaderModuleCreateInfo->byteCode; - - vulkanResult = renderer->vkCreateShaderModule( - renderer->logicalDevice, - &vkShaderModuleCreateInfo, - NULL, - &vulkanShaderModule->shaderModule - ); - - if (vulkanResult != VK_SUCCESS) - { - SDL_free(vulkanShaderModule); - LogVulkanResultAsError("vkCreateShaderModule", vulkanResult); - Refresh_LogError("Failed to create shader module!"); - return NULL; - } - - SDL_AtomicSet(&vulkanShaderModule->referenceCount, 0); - - return (Refresh_ShaderModule*) vulkanShaderModule; -} - -static Refresh_Texture* VULKAN_CreateTexture( - Refresh_Renderer *driverData, - Refresh_TextureCreateInfo *textureCreateInfo -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VkImageUsageFlags imageUsageFlags = ( - VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT - ); - VkImageAspectFlags imageAspectFlags; - uint8_t isDepthFormat = IsRefreshDepthFormat(textureCreateInfo->format); - VkFormat format; - VulkanTextureContainer *container; - VulkanTexture *vulkanTexture; - - if (isDepthFormat) - { - format = RefreshToVK_DepthFormat(renderer, textureCreateInfo->format); - } - else - { - format = RefreshToVK_SurfaceFormat[textureCreateInfo->format]; - } - - if (textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_SAMPLER_BIT) - { - imageUsageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT; - } - - if (textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT) - { - imageUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - } - - if (textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) - { - imageUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - } - - if (textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_COMPUTE_BIT) - { - imageUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT; - } - - if (isDepthFormat) - { - imageAspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT; - - if (IsStencilFormat(format)) - { - imageAspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - } - else - { - imageAspectFlags = VK_IMAGE_ASPECT_COLOR_BIT; - } - - vulkanTexture = VULKAN_INTERNAL_CreateTexture( - renderer, - textureCreateInfo->width, - textureCreateInfo->height, - textureCreateInfo->depth, - textureCreateInfo->isCube, - textureCreateInfo->levelCount, - isDepthFormat ? - textureCreateInfo->sampleCount : /* depth textures do not have a separate msaaTex */ - REFRESH_SAMPLECOUNT_1, - format, - imageAspectFlags, - imageUsageFlags - ); - - /* create the MSAA texture for color attachments, if needed */ - if ( vulkanTexture != NULL && - !isDepthFormat && - textureCreateInfo->sampleCount > REFRESH_SAMPLECOUNT_1 ) - { - vulkanTexture->msaaTex = VULKAN_INTERNAL_CreateTexture( - renderer, - textureCreateInfo->width, - textureCreateInfo->height, - textureCreateInfo->depth, - textureCreateInfo->isCube, - textureCreateInfo->levelCount, - textureCreateInfo->sampleCount, - format, - imageAspectFlags, - imageUsageFlags - ); - } - - container = SDL_malloc(sizeof(VulkanTextureContainer)); - container->vulkanTexture = vulkanTexture; - vulkanTexture->container = container; - - return (Refresh_Texture*) container; -} - -static Refresh_Buffer* VULKAN_CreateBuffer( - Refresh_Renderer *driverData, - Refresh_BufferUsageFlags usageFlags, - uint32_t sizeInBytes -) { - VulkanResourceAccessType resourceAccessType; - VkBufferUsageFlags vulkanUsageFlags = - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - - if (usageFlags == 0) - { - resourceAccessType = RESOURCE_ACCESS_TRANSFER_READ_WRITE; - } - - if (usageFlags & REFRESH_BUFFERUSAGE_VERTEX_BIT) - { - vulkanUsageFlags |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - resourceAccessType = RESOURCE_ACCESS_VERTEX_BUFFER; - } - - if (usageFlags & REFRESH_BUFFERUSAGE_INDEX_BIT) - { - vulkanUsageFlags |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - resourceAccessType = RESOURCE_ACCESS_INDEX_BUFFER; - } - - if (usageFlags & REFRESH_BUFFERUSAGE_COMPUTE_BIT) - { - vulkanUsageFlags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - resourceAccessType = RESOURCE_ACCESS_COMPUTE_SHADER_BUFFER_READ_WRITE; - } - - if (usageFlags & REFRESH_BUFFERUSAGE_INDIRECT_BIT) - { - vulkanUsageFlags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; - resourceAccessType = RESOURCE_ACCESS_INDIRECT_BUFFER; - } - - return (Refresh_Buffer*) VULKAN_INTERNAL_CreateBufferContainer( - (VulkanRenderer*) driverData, - sizeInBytes, - resourceAccessType, - vulkanUsageFlags - ); -} - -/* Setters */ - -static VulkanTransferBuffer* VULKAN_INTERNAL_AcquireTransferBuffer( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer, - VkDeviceSize requiredSize, - VkDeviceSize alignment -) { - VkDeviceSize size; - VkDeviceSize offset; - uint32_t i; - VulkanTransferBuffer *transferBuffer; - - /* Search the command buffer's current transfer buffers */ - - for (i = 0; i < commandBuffer->transferBufferCount; i += 1) - { - transferBuffer = commandBuffer->transferBuffers[i]; - offset = transferBuffer->offset + alignment - (transferBuffer->offset % alignment); - - if (offset + requiredSize <= transferBuffer->buffer->size) - { - transferBuffer->offset = offset; - return transferBuffer; - } - } - - /* Nothing fits, can we get a transfer buffer from the pool? */ - - SDL_LockMutex(renderer->transferBufferPool.lock); - - for (i = 0; i < renderer->transferBufferPool.availableBufferCount; i += 1) - { - transferBuffer = renderer->transferBufferPool.availableBuffers[i]; - offset = transferBuffer->offset + alignment - (transferBuffer->offset % alignment); - - if (offset + requiredSize <= transferBuffer->buffer->size) - { - if (commandBuffer->transferBufferCount == commandBuffer->transferBufferCapacity) - { - commandBuffer->transferBufferCapacity *= 2; - commandBuffer->transferBuffers = SDL_realloc( - commandBuffer->transferBuffers, - commandBuffer->transferBufferCapacity * sizeof(VulkanTransferBuffer*) - ); - } - - commandBuffer->transferBuffers[commandBuffer->transferBufferCount] = transferBuffer; - commandBuffer->transferBufferCount += 1; - - renderer->transferBufferPool.availableBuffers[i] = renderer->transferBufferPool.availableBuffers[renderer->transferBufferPool.availableBufferCount - 1]; - renderer->transferBufferPool.availableBufferCount -= 1; - SDL_UnlockMutex(renderer->transferBufferPool.lock); - - transferBuffer->offset = offset; - return transferBuffer; - } - } - - SDL_UnlockMutex(renderer->transferBufferPool.lock); - - /* Nothing fits, so let's create a new transfer buffer */ - - size = TRANSFER_BUFFER_STARTING_SIZE; - - while (size < requiredSize) - { - size *= 2; - } - - transferBuffer = SDL_malloc(sizeof(VulkanTransferBuffer)); - transferBuffer->offset = 0; - transferBuffer->buffer = VULKAN_INTERNAL_CreateBuffer( - renderer, - size, - RESOURCE_ACCESS_TRANSFER_READ_WRITE, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - 1, - 0, - 1 - ); - transferBuffer->fromPool = 0; - - if (transferBuffer->buffer == NULL) - { - Refresh_LogError("Failed to allocate transfer buffer!"); - SDL_free(transferBuffer); - return NULL; - } - - if (commandBuffer->transferBufferCount == commandBuffer->transferBufferCapacity) - { - commandBuffer->transferBufferCapacity *= 2; - commandBuffer->transferBuffers = SDL_realloc( - commandBuffer->transferBuffers, - commandBuffer->transferBufferCapacity * sizeof(VulkanTransferBuffer*) - ); - } - - commandBuffer->transferBuffers[commandBuffer->transferBufferCount] = transferBuffer; - commandBuffer->transferBufferCount += 1; - - return transferBuffer; -} - -static void VULKAN_SetTextureData( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *textureSlice, - void *data, - uint32_t dataLengthInBytes -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanTexture *vulkanTexture = ((VulkanTextureContainer*) textureSlice->texture)->vulkanTexture; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanTransferBuffer *transferBuffer; - VkBufferImageCopy imageCopy; - uint8_t *stagingBufferPointer; - uint32_t blockSize = VULKAN_INTERNAL_TextureBlockSize(vulkanTexture->format); - uint32_t bufferRowLength; - uint32_t bufferImageHeight; - - transferBuffer = VULKAN_INTERNAL_AcquireTransferBuffer( - renderer, - vulkanCommandBuffer, - VULKAN_INTERNAL_BytesPerImage( - textureSlice->rectangle.w, - textureSlice->rectangle.h, - vulkanTexture->format - ), - VULKAN_INTERNAL_BytesPerPixel(vulkanTexture->format) - ); - - if (transferBuffer == NULL) - { - return; - } - - stagingBufferPointer = - transferBuffer->buffer->usedRegion->allocation->mapPointer + - transferBuffer->buffer->usedRegion->resourceOffset + - transferBuffer->offset; - - SDL_memcpy( - stagingBufferPointer, - data, - dataLengthInBytes - ); - - /* TODO: is it worth it to only transition the specific subresource? */ - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - vulkanTexture->layerCount, - 0, - vulkanTexture->levelCount, - 0, - vulkanTexture->image, - &vulkanTexture->resourceAccessType - ); - - bufferRowLength = SDL_max(blockSize, textureSlice->rectangle.w); - bufferImageHeight = SDL_max(blockSize, textureSlice->rectangle.h); - - imageCopy.imageExtent.width = textureSlice->rectangle.w; - imageCopy.imageExtent.height = textureSlice->rectangle.h; - imageCopy.imageExtent.depth = 1; - imageCopy.imageOffset.x = textureSlice->rectangle.x; - imageCopy.imageOffset.y = textureSlice->rectangle.y; - imageCopy.imageOffset.z = textureSlice->depth; - imageCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageCopy.imageSubresource.baseArrayLayer = textureSlice->layer; - imageCopy.imageSubresource.layerCount = 1; - imageCopy.imageSubresource.mipLevel = textureSlice->level; - imageCopy.bufferOffset = transferBuffer->offset; - imageCopy.bufferRowLength = bufferRowLength; - imageCopy.bufferImageHeight = bufferImageHeight; - - renderer->vkCmdCopyBufferToImage( - vulkanCommandBuffer->commandBuffer, - transferBuffer->buffer->buffer, - vulkanTexture->image, - AccessMap[vulkanTexture->resourceAccessType].imageLayout, - 1, - &imageCopy - ); - - transferBuffer->offset += dataLengthInBytes; - - if (vulkanTexture->usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) - { - /* TODO: is it worth it to only transition the specific subresource? */ - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_ANY_SHADER_READ_SAMPLED_IMAGE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - vulkanTexture->layerCount, - 0, - vulkanTexture->levelCount, - 0, - vulkanTexture->image, - &vulkanTexture->resourceAccessType - ); - } - - VULKAN_INTERNAL_TrackTexture(renderer, vulkanCommandBuffer, vulkanTexture); -} - -static void VULKAN_SetTextureDataYUV( - Refresh_Renderer *driverData, - Refresh_CommandBuffer* commandBuffer, - Refresh_Texture *y, - Refresh_Texture *u, - Refresh_Texture *v, - uint32_t yWidth, - uint32_t yHeight, - uint32_t uvWidth, - uint32_t uvHeight, - void *yDataPtr, - void *uDataPtr, - void *vDataPtr, - uint32_t yDataLength, - uint32_t uvDataLength, - uint32_t yStride, - uint32_t uvStride -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanTexture *tex = ((VulkanTextureContainer*) y)->vulkanTexture; - - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*)commandBuffer; - VulkanTransferBuffer *transferBuffer; - VkBufferImageCopy imageCopy; - uint8_t * stagingBufferPointer; - - transferBuffer = VULKAN_INTERNAL_AcquireTransferBuffer( - renderer, - vulkanCommandBuffer, - yDataLength + uvDataLength, - VULKAN_INTERNAL_BytesPerPixel(tex->format) - ); - - if (transferBuffer == NULL) - { - return; - } - - stagingBufferPointer = - transferBuffer->buffer->usedRegion->allocation->mapPointer + - transferBuffer->buffer->usedRegion->resourceOffset + - transferBuffer->offset; - - /* Initialize values that are the same for Y, U, and V */ - - imageCopy.imageExtent.depth = 1; - imageCopy.imageOffset.x = 0; - imageCopy.imageOffset.y = 0; - imageCopy.imageOffset.z = 0; - imageCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageCopy.imageSubresource.baseArrayLayer = 0; - imageCopy.imageSubresource.layerCount = 1; - imageCopy.imageSubresource.mipLevel = 0; - - /* Y */ - - SDL_memcpy( - stagingBufferPointer, - yDataPtr, - yDataLength - ); - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - tex->layerCount, - 0, - tex->levelCount, - 0, - tex->image, - &tex->resourceAccessType - ); - - - imageCopy.imageExtent.width = yWidth; - imageCopy.imageExtent.height = yHeight; - imageCopy.bufferOffset = transferBuffer->offset; - imageCopy.bufferRowLength = yStride; - imageCopy.bufferImageHeight = yHeight; - - renderer->vkCmdCopyBufferToImage( - vulkanCommandBuffer->commandBuffer, - transferBuffer->buffer->buffer, - tex->image, - AccessMap[tex->resourceAccessType].imageLayout, - 1, - &imageCopy - ); - - if (tex->usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) - { - /* TODO: is it worth it to only transition the specific subresource? */ - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_ANY_SHADER_READ_SAMPLED_IMAGE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - tex->layerCount, - 0, - tex->levelCount, - 0, - tex->image, - &tex->resourceAccessType - ); - } - - VULKAN_INTERNAL_TrackTexture(renderer, vulkanCommandBuffer, tex); - - /* These apply to both U and V */ - - imageCopy.imageExtent.width = uvWidth; - imageCopy.imageExtent.height = uvHeight; - imageCopy.bufferRowLength = uvStride; - imageCopy.bufferImageHeight = uvHeight; - - /* U */ - - imageCopy.bufferOffset = transferBuffer->offset + yDataLength; - - tex = ((VulkanTextureContainer*) u)->vulkanTexture; - - SDL_memcpy( - stagingBufferPointer + yDataLength, - uDataPtr, - uvDataLength - ); - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - tex->layerCount, - 0, - tex->levelCount, - 0, - tex->image, - &tex->resourceAccessType - ); - - renderer->vkCmdCopyBufferToImage( - vulkanCommandBuffer->commandBuffer, - transferBuffer->buffer->buffer, - tex->image, - AccessMap[tex->resourceAccessType].imageLayout, - 1, - &imageCopy - ); - - if (tex->usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) - { - /* TODO: is it worth it to only transition the specific subresource? */ - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_ANY_SHADER_READ_SAMPLED_IMAGE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - tex->layerCount, - 0, - tex->levelCount, - 0, - tex->image, - &tex->resourceAccessType - ); - } - - VULKAN_INTERNAL_TrackTexture(renderer, vulkanCommandBuffer, tex); - - /* V */ - - imageCopy.bufferOffset = transferBuffer->offset + yDataLength + uvDataLength; - - tex = ((VulkanTextureContainer*) v)->vulkanTexture; - - SDL_memcpy( - stagingBufferPointer + yDataLength + uvDataLength, - vDataPtr, - uvDataLength - ); - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - tex->layerCount, - 0, - tex->levelCount, - 0, - tex->image, - &tex->resourceAccessType - ); - - renderer->vkCmdCopyBufferToImage( - vulkanCommandBuffer->commandBuffer, - transferBuffer->buffer->buffer, - tex->image, - AccessMap[tex->resourceAccessType].imageLayout, - 1, - &imageCopy - ); - - transferBuffer->offset += yDataLength + uvDataLength; - - if (tex->usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) - { - /* TODO: is it worth it to only transition the specific subresource? */ - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_ANY_SHADER_READ_SAMPLED_IMAGE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - tex->layerCount, - 0, - tex->levelCount, - 0, - tex->image, - &tex->resourceAccessType - ); - } - - VULKAN_INTERNAL_TrackTexture(renderer, vulkanCommandBuffer, tex); -} - -static void VULKAN_INTERNAL_BlitImage( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer, - Refresh_TextureSlice *sourceTextureSlice, - Refresh_TextureSlice *destinationTextureSlice, - VkFilter filter -) { - VkImageBlit blit; - VulkanTexture *sourceTexture = ((VulkanTextureContainer*) sourceTextureSlice->texture)->vulkanTexture; - VulkanTexture *destinationTexture = ((VulkanTextureContainer*) destinationTextureSlice->texture)->vulkanTexture; - - VulkanResourceAccessType originalSourceAccessType = sourceTexture->resourceAccessType; - VulkanResourceAccessType originalDestinationAccessType = destinationTexture->resourceAccessType; - - if (originalDestinationAccessType == RESOURCE_ACCESS_NONE) - { - originalDestinationAccessType = RESOURCE_ACCESS_ANY_SHADER_READ_SAMPLED_IMAGE; - } - - /* TODO: is it worth it to only transition the specific subresource? */ - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - commandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_READ, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - sourceTexture->layerCount, - 0, - sourceTexture->levelCount, - 0, - sourceTexture->image, - &sourceTexture->resourceAccessType - ); - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - commandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - destinationTexture->layerCount, - 0, - destinationTexture->levelCount, - 0, - destinationTexture->image, - &destinationTexture->resourceAccessType - ); - - blit.srcOffsets[0].x = sourceTextureSlice->rectangle.x; - blit.srcOffsets[0].y = sourceTextureSlice->rectangle.y; - blit.srcOffsets[0].z = sourceTextureSlice->depth; - blit.srcOffsets[1].x = sourceTextureSlice->rectangle.x + sourceTextureSlice->rectangle.w; - blit.srcOffsets[1].y = sourceTextureSlice->rectangle.y + sourceTextureSlice->rectangle.h; - blit.srcOffsets[1].z = 1; - - blit.srcSubresource.mipLevel = sourceTextureSlice->level; - blit.srcSubresource.baseArrayLayer = sourceTextureSlice->layer; - blit.srcSubresource.layerCount = 1; - blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - - blit.dstOffsets[0].x = destinationTextureSlice->rectangle.x; - blit.dstOffsets[0].y = destinationTextureSlice->rectangle.y; - blit.dstOffsets[0].z = destinationTextureSlice->depth; - blit.dstOffsets[1].x = destinationTextureSlice->rectangle.x + destinationTextureSlice->rectangle.w; - blit.dstOffsets[1].y = destinationTextureSlice->rectangle.y + destinationTextureSlice->rectangle.h; - blit.dstOffsets[1].z = 1; - - blit.dstSubresource.mipLevel = destinationTextureSlice->level; - blit.dstSubresource.baseArrayLayer = destinationTextureSlice->layer; - blit.dstSubresource.layerCount = 1; - blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - - renderer->vkCmdBlitImage( - commandBuffer->commandBuffer, - sourceTexture->image, - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - destinationTexture->image, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - &blit, - filter - ); - - /* TODO: is it worth it to only transition the specific subresource? */ - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - commandBuffer->commandBuffer, - originalSourceAccessType, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - sourceTexture->layerCount, - 0, - sourceTexture->levelCount, - 0, - sourceTexture->image, - &sourceTexture->resourceAccessType - ); - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - commandBuffer->commandBuffer, - originalDestinationAccessType, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - destinationTexture->layerCount, - 0, - destinationTexture->levelCount, - 0, - destinationTexture->image, - &destinationTexture->resourceAccessType - ); - - VULKAN_INTERNAL_TrackTexture(renderer, commandBuffer, sourceTexture); - VULKAN_INTERNAL_TrackTexture(renderer, commandBuffer, destinationTexture); -} - -REFRESHAPI void VULKAN_CopyTextureToTexture( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *sourceTextureSlice, - Refresh_TextureSlice *destinationTextureSlice, - Refresh_Filter filter -) { - VulkanRenderer *renderer = (VulkanRenderer*)driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - - VULKAN_INTERNAL_BlitImage( - renderer, - vulkanCommandBuffer, - sourceTextureSlice, - destinationTextureSlice, - RefreshToVK_Filter[filter] - ); -} - -static void VULKAN_INTERNAL_SetBufferData( - VulkanBuffer* vulkanBuffer, - VkDeviceSize offsetInBytes, - void* data, - uint32_t dataLength -) { - SDL_memcpy( - vulkanBuffer->usedRegion->allocation->mapPointer + vulkanBuffer->usedRegion->resourceOffset + offsetInBytes, - data, - dataLength - ); -} - -static void VULKAN_SetBufferData( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint32_t offsetInBytes, - void* data, - uint32_t dataLength -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer* vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanBuffer* vulkanBuffer = ((VulkanBufferContainer*) buffer)->vulkanBuffer; - VulkanTransferBuffer* transferBuffer; - uint8_t* transferBufferPointer; - VkBufferCopy bufferCopy; - VulkanResourceAccessType accessType = vulkanBuffer->resourceAccessType; - - transferBuffer = VULKAN_INTERNAL_AcquireTransferBuffer( - renderer, - vulkanCommandBuffer, - dataLength, - renderer->physicalDeviceProperties.properties.limits.optimalBufferCopyOffsetAlignment - ); - - if (transferBuffer == NULL) - { - return; - } - - transferBufferPointer = - transferBuffer->buffer->usedRegion->allocation->mapPointer + - transferBuffer->buffer->usedRegion->resourceOffset + - transferBuffer->offset; - - SDL_memcpy( - transferBufferPointer, - data, - dataLength - ); - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_READ, - transferBuffer->buffer - ); - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - vulkanBuffer - ); - - bufferCopy.srcOffset = transferBuffer->offset; - bufferCopy.dstOffset = offsetInBytes; - bufferCopy.size = (VkDeviceSize) dataLength; - - renderer->vkCmdCopyBuffer( - vulkanCommandBuffer->commandBuffer, - transferBuffer->buffer->buffer, - vulkanBuffer->buffer, - 1, - &bufferCopy - ); - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - accessType, - vulkanBuffer - ); - - transferBuffer->offset += dataLength; - - VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, vulkanBuffer); -} - -/* FIXME: this should return uint64_t */ -static uint32_t VULKAN_PushVertexShaderUniforms( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - void *data, - uint32_t dataLengthInBytes -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer* vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanGraphicsPipeline* graphicsPipeline = vulkanCommandBuffer->currentGraphicsPipeline; - uint32_t offset; - - if (graphicsPipeline == NULL) - { - Refresh_LogError("Cannot push uniforms if a pipeline is not bound!"); - return 0; - } - - if (graphicsPipeline->vertexUniformBlockSize == 0) - { - Refresh_LogError("Bound pipeline's vertex stage does not declare uniforms!"); - return 0; - } - - if ( - vulkanCommandBuffer->vertexUniformBuffer->offset + - graphicsPipeline->vertexUniformBlockSize >= - UBO_SECTION_SIZE - ) { - /* We're out of space in this buffer, bind the old one and acquire a new one */ - VULKAN_INTERNAL_BindUniformBuffer( - renderer, - vulkanCommandBuffer, - vulkanCommandBuffer->vertexUniformBuffer - ); - vulkanCommandBuffer->vertexUniformBuffer = VULKAN_INTERNAL_AcquireUniformBufferFromPool( - renderer, - renderer->vertexUniformBufferPool, - graphicsPipeline->vertexUniformBlockSize - ); - } - - offset = vulkanCommandBuffer->vertexUniformBuffer->offset; - - VULKAN_INTERNAL_SetBufferData( - vulkanCommandBuffer->vertexUniformBuffer->pool->buffer, - vulkanCommandBuffer->vertexUniformBuffer->poolOffset + vulkanCommandBuffer->vertexUniformBuffer->offset, - data, - dataLengthInBytes - ); - - vulkanCommandBuffer->vertexUniformBuffer->offset += graphicsPipeline->vertexUniformBlockSize; - - return offset; -} - -/* FIXME: this should return uint64_t */ -static uint32_t VULKAN_PushFragmentShaderUniforms( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - void *data, - uint32_t dataLengthInBytes -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer* vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanGraphicsPipeline* graphicsPipeline = vulkanCommandBuffer->currentGraphicsPipeline; - uint32_t offset; - - if ( - vulkanCommandBuffer->fragmentUniformBuffer->offset + - graphicsPipeline->fragmentUniformBlockSize >= - UBO_SECTION_SIZE - ) { - /* We're out of space in this buffer, bind the old one and acquire a new one */ - VULKAN_INTERNAL_BindUniformBuffer( - renderer, - vulkanCommandBuffer, - vulkanCommandBuffer->fragmentUniformBuffer - ); - vulkanCommandBuffer->fragmentUniformBuffer = VULKAN_INTERNAL_AcquireUniformBufferFromPool( - renderer, - renderer->fragmentUniformBufferPool, - graphicsPipeline->fragmentUniformBlockSize - ); - } - - offset = vulkanCommandBuffer->fragmentUniformBuffer->offset; - - VULKAN_INTERNAL_SetBufferData( - vulkanCommandBuffer->fragmentUniformBuffer->pool->buffer, - vulkanCommandBuffer->fragmentUniformBuffer->poolOffset + vulkanCommandBuffer->fragmentUniformBuffer->offset, - data, - dataLengthInBytes - ); - - vulkanCommandBuffer->fragmentUniformBuffer->offset += graphicsPipeline->fragmentUniformBlockSize; - - return offset; -} - -static uint32_t VULKAN_PushComputeShaderUniforms( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - void *data, - uint32_t dataLengthInBytes -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer* vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanComputePipeline* computePipeline = vulkanCommandBuffer->currentComputePipeline; - uint32_t offset; - - if ( - vulkanCommandBuffer->computeUniformBuffer->offset + - computePipeline->uniformBlockSize >= - UBO_SECTION_SIZE - ) { - /* We're out of space in this buffer, bind the old one and acquire a new one */ - VULKAN_INTERNAL_BindUniformBuffer( - renderer, - vulkanCommandBuffer, - vulkanCommandBuffer->computeUniformBuffer - ); - vulkanCommandBuffer->computeUniformBuffer = VULKAN_INTERNAL_AcquireUniformBufferFromPool( - renderer, - renderer->computeUniformBufferPool, - computePipeline->uniformBlockSize - ); - } - - offset = vulkanCommandBuffer->computeUniformBuffer->offset; - - VULKAN_INTERNAL_SetBufferData( - vulkanCommandBuffer->computeUniformBuffer->pool->buffer, - vulkanCommandBuffer->computeUniformBuffer->poolOffset + vulkanCommandBuffer->computeUniformBuffer->offset, - data, - dataLengthInBytes - ); - - vulkanCommandBuffer->computeUniformBuffer->offset += computePipeline->uniformBlockSize; - - return offset; -} - -/* If fetching an image descriptor, descriptorImageInfos must not be NULL. - * If fetching a buffer descriptor, descriptorBufferInfos must not be NULL. - */ -static VkDescriptorSet VULKAN_INTERNAL_FetchDescriptorSet( - VulkanRenderer *renderer, - VulkanCommandBuffer *vulkanCommandBuffer, - DescriptorSetCache *descriptorSetCache, - VkDescriptorImageInfo *descriptorImageInfos, /* Can be NULL */ - VkDescriptorBufferInfo *descriptorBufferInfos /* Can be NULL */ -) { - uint32_t i; - VkDescriptorSet descriptorSet; - VkWriteDescriptorSet writeDescriptorSets[MAX_TEXTURE_SAMPLERS]; - uint8_t isImage; - - if (descriptorImageInfos == NULL && descriptorBufferInfos == NULL) - { - Refresh_LogError("descriptorImageInfos and descriptorBufferInfos cannot both be NULL!"); - return VK_NULL_HANDLE; - } - else if (descriptorImageInfos != NULL && descriptorBufferInfos != NULL) - { - Refresh_LogError("descriptorImageInfos and descriptorBufferInfos cannot both be set!"); - return VK_NULL_HANDLE; - } - - isImage = descriptorImageInfos != NULL; - - SDL_LockMutex(descriptorSetCache->lock); - - /* If no inactive descriptor sets remain, create a new pool and allocate new inactive sets */ - - if (descriptorSetCache->inactiveDescriptorSetCount == 0) - { - descriptorSetCache->descriptorPoolCount += 1; - descriptorSetCache->descriptorPools = SDL_realloc( - descriptorSetCache->descriptorPools, - sizeof(VkDescriptorPool) * descriptorSetCache->descriptorPoolCount - ); - - if (!VULKAN_INTERNAL_CreateDescriptorPool( - renderer, - descriptorSetCache->descriptorType, - descriptorSetCache->nextPoolSize, - descriptorSetCache->nextPoolSize * descriptorSetCache->bindingCount, - &descriptorSetCache->descriptorPools[descriptorSetCache->descriptorPoolCount - 1] - )) { - SDL_UnlockMutex(descriptorSetCache->lock); - Refresh_LogError("Failed to create descriptor pool!"); - return VK_NULL_HANDLE; - } - - descriptorSetCache->inactiveDescriptorSetCapacity += descriptorSetCache->nextPoolSize; - - descriptorSetCache->inactiveDescriptorSets = SDL_realloc( - descriptorSetCache->inactiveDescriptorSets, - sizeof(VkDescriptorSet) * descriptorSetCache->inactiveDescriptorSetCapacity - ); - - if (!VULKAN_INTERNAL_AllocateDescriptorSets( - renderer, - descriptorSetCache->descriptorPools[descriptorSetCache->descriptorPoolCount - 1], - descriptorSetCache->descriptorSetLayout, - descriptorSetCache->nextPoolSize, - descriptorSetCache->inactiveDescriptorSets - )) { - SDL_UnlockMutex(descriptorSetCache->lock); - Refresh_LogError("Failed to allocate descriptor sets!"); - return VK_NULL_HANDLE; - } - - descriptorSetCache->inactiveDescriptorSetCount = descriptorSetCache->nextPoolSize; - - descriptorSetCache->nextPoolSize *= 2; - } - - descriptorSet = descriptorSetCache->inactiveDescriptorSets[descriptorSetCache->inactiveDescriptorSetCount - 1]; - descriptorSetCache->inactiveDescriptorSetCount -= 1; - - for (i = 0; i < descriptorSetCache->bindingCount; i += 1) - { - writeDescriptorSets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeDescriptorSets[i].pNext = NULL; - writeDescriptorSets[i].descriptorCount = 1; - writeDescriptorSets[i].descriptorType = descriptorSetCache->descriptorType; - writeDescriptorSets[i].dstArrayElement = 0; - writeDescriptorSets[i].dstBinding = i; - writeDescriptorSets[i].dstSet = descriptorSet; - writeDescriptorSets[i].pTexelBufferView = NULL; - - if (isImage) - { - writeDescriptorSets[i].pImageInfo = &descriptorImageInfos[i]; - writeDescriptorSets[i].pBufferInfo = NULL; - - } - else - { - writeDescriptorSets[i].pBufferInfo = &descriptorBufferInfos[i]; - writeDescriptorSets[i].pImageInfo = NULL; - } - } - - renderer->vkUpdateDescriptorSets( - renderer->logicalDevice, - descriptorSetCache->bindingCount, - writeDescriptorSets, - 0, - NULL - ); - - SDL_UnlockMutex(descriptorSetCache->lock); - - if (vulkanCommandBuffer->boundDescriptorSetDataCount == vulkanCommandBuffer->boundDescriptorSetDataCapacity) - { - vulkanCommandBuffer->boundDescriptorSetDataCapacity *= 2; - vulkanCommandBuffer->boundDescriptorSetDatas = SDL_realloc( - vulkanCommandBuffer->boundDescriptorSetDatas, - vulkanCommandBuffer->boundDescriptorSetDataCapacity * sizeof(DescriptorSetData) - ); - } - - vulkanCommandBuffer->boundDescriptorSetDatas[vulkanCommandBuffer->boundDescriptorSetDataCount].descriptorSet = descriptorSet; - vulkanCommandBuffer->boundDescriptorSetDatas[vulkanCommandBuffer->boundDescriptorSetDataCount].descriptorSetCache = descriptorSetCache; - vulkanCommandBuffer->boundDescriptorSetDataCount += 1; - - return descriptorSet; -} - -static void VULKAN_BindVertexSamplers( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures, - Refresh_Sampler **pSamplers -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanGraphicsPipeline *graphicsPipeline = vulkanCommandBuffer->currentGraphicsPipeline; - - VulkanTexture *currentTexture; - VulkanSampler *currentSampler; - uint32_t i, samplerCount; - VkDescriptorImageInfo descriptorImageInfos[MAX_TEXTURE_SAMPLERS]; - - if (graphicsPipeline->pipelineLayout->vertexSamplerDescriptorSetCache == NULL) - { - return; - } - - samplerCount = graphicsPipeline->pipelineLayout->vertexSamplerDescriptorSetCache->bindingCount; - - for (i = 0; i < samplerCount; i += 1) - { - currentTexture = ((VulkanTextureContainer*) pTextures[i])->vulkanTexture; - currentSampler = (VulkanSampler*) pSamplers[i]; - descriptorImageInfos[i].imageView = currentTexture->view; - descriptorImageInfos[i].sampler = currentSampler->sampler; - descriptorImageInfos[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - VULKAN_INTERNAL_TrackTexture(renderer, vulkanCommandBuffer, currentTexture); - VULKAN_INTERNAL_TrackSampler(renderer, vulkanCommandBuffer, currentSampler); - } - - vulkanCommandBuffer->vertexSamplerDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet( - renderer, - vulkanCommandBuffer, - graphicsPipeline->pipelineLayout->vertexSamplerDescriptorSetCache, - descriptorImageInfos, - NULL - ); -} - -static void VULKAN_BindFragmentSamplers( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures, - Refresh_Sampler **pSamplers -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanGraphicsPipeline *graphicsPipeline = vulkanCommandBuffer->currentGraphicsPipeline; - - VulkanTexture *currentTexture; - VulkanSampler *currentSampler; - uint32_t i, samplerCount; - VkDescriptorImageInfo descriptorImageInfos[MAX_TEXTURE_SAMPLERS]; - - if (graphicsPipeline->pipelineLayout->fragmentSamplerDescriptorSetCache == NULL) - { - return; - } - - samplerCount = graphicsPipeline->pipelineLayout->fragmentSamplerDescriptorSetCache->bindingCount; - - for (i = 0; i < samplerCount; i += 1) - { - currentTexture = ((VulkanTextureContainer*) pTextures[i])->vulkanTexture; - currentSampler = (VulkanSampler*) pSamplers[i]; - descriptorImageInfos[i].imageView = currentTexture->view; - descriptorImageInfos[i].sampler = currentSampler->sampler; - descriptorImageInfos[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - VULKAN_INTERNAL_TrackTexture(renderer, vulkanCommandBuffer, currentTexture); - VULKAN_INTERNAL_TrackSampler(renderer, vulkanCommandBuffer, currentSampler); - } - - vulkanCommandBuffer->fragmentSamplerDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet( - renderer, - vulkanCommandBuffer, - graphicsPipeline->pipelineLayout->fragmentSamplerDescriptorSetCache, - descriptorImageInfos, - NULL - ); -} - -static void VULKAN_GetBufferData( - Refresh_Renderer *driverData, - Refresh_Buffer *buffer, - void *data, - uint32_t dataLengthInBytes -) { - VulkanBuffer* vulkanBuffer = ((VulkanBufferContainer*) buffer)->vulkanBuffer; - uint8_t *dataPtr = (uint8_t*) data; - uint8_t *mapPointer; - - mapPointer = - vulkanBuffer->usedRegion->allocation->mapPointer + - vulkanBuffer->usedRegion->resourceOffset; - - SDL_memcpy( - dataPtr, - mapPointer, - dataLengthInBytes - ); -} - -static void VULKAN_CopyTextureToBuffer( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_TextureSlice *textureSlice, - Refresh_Buffer *buffer -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanTexture *vulkanTexture = ((VulkanTextureContainer*) textureSlice->texture)->vulkanTexture; - VulkanBuffer* vulkanBuffer = ((VulkanBufferContainer*) buffer)->vulkanBuffer; - - VulkanResourceAccessType prevResourceAccess; - VkBufferImageCopy imageCopy; - - /* Cache this so we can restore it later */ - prevResourceAccess = vulkanTexture->resourceAccessType; - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_READ, - VK_IMAGE_ASPECT_COLOR_BIT, - textureSlice->layer, - 1, - textureSlice->level, - 1, - 0, - vulkanTexture->image, - &vulkanTexture->resourceAccessType - ); - - /* Save texture data to buffer */ - - imageCopy.imageExtent.width = textureSlice->rectangle.w; - imageCopy.imageExtent.height = textureSlice->rectangle.h; - imageCopy.imageExtent.depth = 1; - imageCopy.bufferRowLength = textureSlice->rectangle.w; - imageCopy.bufferImageHeight = textureSlice->rectangle.h; - imageCopy.imageOffset.x = textureSlice->rectangle.x; - imageCopy.imageOffset.y = textureSlice->rectangle.y; - imageCopy.imageOffset.z = textureSlice->depth; - imageCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageCopy.imageSubresource.baseArrayLayer = textureSlice->layer; - imageCopy.imageSubresource.layerCount = 1; - imageCopy.imageSubresource.mipLevel = textureSlice->level; - imageCopy.bufferOffset = 0; - - renderer->vkCmdCopyImageToBuffer( - vulkanCommandBuffer->commandBuffer, - vulkanTexture->image, - AccessMap[vulkanTexture->resourceAccessType].imageLayout, - vulkanBuffer->buffer, - 1, - &imageCopy - ); - - /* Restore the image layout */ - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - prevResourceAccess, - VK_IMAGE_ASPECT_COLOR_BIT, - textureSlice->layer, - 1, - textureSlice->level, - 1, - 0, - vulkanTexture->image, - &vulkanTexture->resourceAccessType - ); - - VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, vulkanBuffer); - VULKAN_INTERNAL_TrackTexture(renderer, vulkanCommandBuffer, vulkanTexture); -} - -static void VULKAN_INTERNAL_QueueDestroyTexture( - VulkanRenderer *renderer, - VulkanTexture *vulkanTexture -) { - SDL_LockMutex(renderer->disposeLock); - - EXPAND_ARRAY_IF_NEEDED( - renderer->texturesToDestroy, - VulkanTexture*, - renderer->texturesToDestroyCount + 1, - renderer->texturesToDestroyCapacity, - renderer->texturesToDestroyCapacity * 2 - ) - - renderer->texturesToDestroy[ - renderer->texturesToDestroyCount - ] = vulkanTexture; - renderer->texturesToDestroyCount += 1; - - SDL_UnlockMutex(renderer->disposeLock); -} - -static void VULKAN_QueueDestroyTexture( - Refresh_Renderer *driverData, - Refresh_Texture *texture -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanTextureContainer *vulkanTextureContainer = (VulkanTextureContainer *)texture; - VulkanTexture *vulkanTexture = vulkanTextureContainer->vulkanTexture; - - SDL_LockMutex(renderer->disposeLock); - - VULKAN_INTERNAL_QueueDestroyTexture(renderer, vulkanTexture); - - /* Containers are just client handles, so we can destroy immediately */ - SDL_free(vulkanTextureContainer); - - SDL_UnlockMutex(renderer->disposeLock); -} - -static void VULKAN_QueueDestroySampler( - Refresh_Renderer *driverData, - Refresh_Sampler *sampler -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanSampler* vulkanSampler = (VulkanSampler*) sampler; - - SDL_LockMutex(renderer->disposeLock); - - EXPAND_ARRAY_IF_NEEDED( - renderer->samplersToDestroy, - VulkanSampler*, - renderer->samplersToDestroyCount + 1, - renderer->samplersToDestroyCapacity, - renderer->samplersToDestroyCapacity * 2 - ) - - renderer->samplersToDestroy[renderer->samplersToDestroyCount] = vulkanSampler; - renderer->samplersToDestroyCount += 1; - - SDL_UnlockMutex(renderer->disposeLock); -} - -static void VULKAN_INTERNAL_QueueDestroyBuffer( - VulkanRenderer *renderer, - VulkanBuffer *vulkanBuffer -) { - SDL_LockMutex(renderer->disposeLock); - - EXPAND_ARRAY_IF_NEEDED( - renderer->buffersToDestroy, - VulkanBuffer*, - renderer->buffersToDestroyCount + 1, - renderer->buffersToDestroyCapacity, - renderer->buffersToDestroyCapacity * 2 - ) - - renderer->buffersToDestroy[ - renderer->buffersToDestroyCount - ] = vulkanBuffer; - renderer->buffersToDestroyCount += 1; - - SDL_UnlockMutex(renderer->disposeLock); -} - -static void VULKAN_QueueDestroyBuffer( - Refresh_Renderer *driverData, - Refresh_Buffer *buffer -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanBufferContainer *vulkanBufferContainer = (VulkanBufferContainer*) buffer; - VulkanBuffer *vulkanBuffer = vulkanBufferContainer->vulkanBuffer; - - SDL_LockMutex(renderer->disposeLock); - - VULKAN_INTERNAL_QueueDestroyBuffer(renderer, vulkanBuffer); - - /* Containers are just client handles, so we can destroy immediately */ - SDL_free(vulkanBufferContainer); - - SDL_UnlockMutex(renderer->disposeLock); -} - -static void VULKAN_QueueDestroyShaderModule( - Refresh_Renderer *driverData, - Refresh_ShaderModule *shaderModule -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanShaderModule *vulkanShaderModule = (VulkanShaderModule*) shaderModule; - - SDL_LockMutex(renderer->disposeLock); - - EXPAND_ARRAY_IF_NEEDED( - renderer->shaderModulesToDestroy, - VulkanShaderModule*, - renderer->shaderModulesToDestroyCount + 1, - renderer->shaderModulesToDestroyCapacity, - renderer->shaderModulesToDestroyCapacity * 2 - ) - - renderer->shaderModulesToDestroy[renderer->shaderModulesToDestroyCount] = vulkanShaderModule; - renderer->shaderModulesToDestroyCount += 1; - - SDL_UnlockMutex(renderer->disposeLock); -} - -static void VULKAN_QueueDestroyComputePipeline( - Refresh_Renderer *driverData, - Refresh_ComputePipeline *computePipeline -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanComputePipeline *vulkanComputePipeline = (VulkanComputePipeline*) computePipeline; - - SDL_LockMutex(renderer->disposeLock); - - EXPAND_ARRAY_IF_NEEDED( - renderer->computePipelinesToDestroy, - VulkanComputePipeline*, - renderer->computePipelinesToDestroyCount + 1, - renderer->computePipelinesToDestroyCapacity, - renderer->computePipelinesToDestroyCapacity * 2 - ) - - renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount] = vulkanComputePipeline; - renderer->computePipelinesToDestroyCount += 1; - - SDL_UnlockMutex(renderer->disposeLock); -} - -static void VULKAN_QueueDestroyGraphicsPipeline( - Refresh_Renderer *driverData, - Refresh_GraphicsPipeline *graphicsPipeline -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanGraphicsPipeline *vulkanGraphicsPipeline = (VulkanGraphicsPipeline*) graphicsPipeline; - - SDL_LockMutex(renderer->disposeLock); - - EXPAND_ARRAY_IF_NEEDED( - renderer->graphicsPipelinesToDestroy, - VulkanGraphicsPipeline*, - renderer->graphicsPipelinesToDestroyCount + 1, - renderer->graphicsPipelinesToDestroyCapacity, - renderer->graphicsPipelinesToDestroyCapacity * 2 - ) - - renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount] = vulkanGraphicsPipeline; - renderer->graphicsPipelinesToDestroyCount += 1; - - SDL_UnlockMutex(renderer->disposeLock); -} - -/* Command Buffer render state */ - -static VkRenderPass VULKAN_INTERNAL_FetchRenderPass( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer, - Refresh_ColorAttachmentInfo *colorAttachmentInfos, - uint32_t colorAttachmentCount, - Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo -) { - VkRenderPass renderPass; - RenderPassHash hash; - uint32_t i; - VulkanTexture *texture; - - SDL_LockMutex(renderer->renderPassFetchLock); - - for (i = 0; i < colorAttachmentCount; i += 1) - { - hash.colorTargetDescriptions[i].format = ((VulkanTextureContainer*) colorAttachmentInfos[i].texture)->vulkanTexture->format; - hash.colorTargetDescriptions[i].clearColor = colorAttachmentInfos[i].clearColor; - hash.colorTargetDescriptions[i].loadOp = colorAttachmentInfos[i].loadOp; - hash.colorTargetDescriptions[i].storeOp = colorAttachmentInfos[i].storeOp; - } - - hash.colorAttachmentSampleCount = REFRESH_SAMPLECOUNT_1; - if (colorAttachmentCount > 0) - { - texture = ((VulkanTextureContainer*) colorAttachmentInfos[0].texture)->vulkanTexture; - if (texture->msaaTex != NULL) - { - hash.colorAttachmentSampleCount = texture->msaaTex->sampleCount; - } - } - - hash.colorAttachmentCount = colorAttachmentCount; - - if (depthStencilAttachmentInfo == NULL) - { - hash.depthStencilTargetDescription.format = 0; - hash.depthStencilTargetDescription.loadOp = REFRESH_LOADOP_DONT_CARE; - hash.depthStencilTargetDescription.storeOp = REFRESH_STOREOP_DONT_CARE; - hash.depthStencilTargetDescription.stencilLoadOp = REFRESH_LOADOP_DONT_CARE; - hash.depthStencilTargetDescription.stencilStoreOp = REFRESH_STOREOP_DONT_CARE; - } - else - { - hash.depthStencilTargetDescription.format = ((VulkanTextureContainer*) depthStencilAttachmentInfo->texture)->vulkanTexture->format; - hash.depthStencilTargetDescription.loadOp = depthStencilAttachmentInfo->loadOp; - hash.depthStencilTargetDescription.storeOp = depthStencilAttachmentInfo->storeOp; - hash.depthStencilTargetDescription.stencilLoadOp = depthStencilAttachmentInfo->stencilLoadOp; - hash.depthStencilTargetDescription.stencilStoreOp = depthStencilAttachmentInfo->stencilStoreOp; - } - - renderPass = RenderPassHashArray_Fetch( - &renderer->renderPassHashArray, - &hash - ); - - if (renderPass != VK_NULL_HANDLE) - { - SDL_UnlockMutex(renderer->renderPassFetchLock); - return renderPass; - } - - renderPass = VULKAN_INTERNAL_CreateRenderPass( - renderer, - commandBuffer, - colorAttachmentInfos, - colorAttachmentCount, - depthStencilAttachmentInfo - ); - - if (renderPass != VK_NULL_HANDLE) - { - RenderPassHashArray_Insert( - &renderer->renderPassHashArray, - hash, - renderPass - ); - } - - SDL_UnlockMutex(renderer->renderPassFetchLock); - return renderPass; -} - -static VulkanFramebuffer* VULKAN_INTERNAL_FetchFramebuffer( - VulkanRenderer *renderer, - VkRenderPass renderPass, - Refresh_ColorAttachmentInfo *colorAttachmentInfos, - uint32_t colorAttachmentCount, - Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo, - uint32_t width, - uint32_t height -) { - VulkanFramebuffer *vulkanFramebuffer; - VkFramebufferCreateInfo framebufferInfo; - VkResult result; - VkImageView imageViewAttachments[2 * MAX_COLOR_TARGET_BINDINGS + 1]; - FramebufferHash hash; - VulkanTexture *texture; - VulkanRenderTarget *renderTarget; - uint32_t attachmentCount = 0; - uint32_t i; - - for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) - { - hash.colorAttachmentViews[i] = VK_NULL_HANDLE; - hash.colorMultiSampleAttachmentViews[i] = VK_NULL_HANDLE; - } - - hash.colorAttachmentCount = colorAttachmentCount; - - for (i = 0; i < colorAttachmentCount; i += 1) - { - texture = ((VulkanTextureContainer*) colorAttachmentInfos[i].texture)->vulkanTexture; - - renderTarget = VULKAN_INTERNAL_FetchRenderTarget( - renderer, - texture, - colorAttachmentInfos[i].depth, - colorAttachmentInfos[i].layer, - colorAttachmentInfos[i].level - ); - - hash.colorAttachmentViews[i] = ( - renderTarget->view - ); - - if (texture->msaaTex != NULL) - { - renderTarget = VULKAN_INTERNAL_FetchRenderTarget( - renderer, - texture->msaaTex, - colorAttachmentInfos[i].depth, - colorAttachmentInfos[i].layer, - colorAttachmentInfos[i].level - ); - - hash.colorMultiSampleAttachmentViews[i] = ( - renderTarget->view - ); - } - } - - if (depthStencilAttachmentInfo == NULL) - { - hash.depthStencilAttachmentView = VK_NULL_HANDLE; - } - else - { - texture = ((VulkanTextureContainer*) depthStencilAttachmentInfo->texture)->vulkanTexture; - renderTarget = VULKAN_INTERNAL_FetchRenderTarget( - renderer, - texture, - depthStencilAttachmentInfo->depth, - depthStencilAttachmentInfo->layer, - depthStencilAttachmentInfo->level - ); - hash.depthStencilAttachmentView = renderTarget->view; - } - - hash.width = width; - hash.height = height; - - SDL_LockMutex(renderer->framebufferFetchLock); - - vulkanFramebuffer = FramebufferHashArray_Fetch( - &renderer->framebufferHashArray, - &hash - ); - - SDL_UnlockMutex(renderer->framebufferFetchLock); - - if (vulkanFramebuffer != NULL) - { - return vulkanFramebuffer; - } - - vulkanFramebuffer = SDL_malloc(sizeof(VulkanFramebuffer)); - - SDL_AtomicSet(&vulkanFramebuffer->referenceCount, 0); - - /* Create a new framebuffer */ - - for (i = 0; i < colorAttachmentCount; i += 1) - { - texture = ((VulkanTextureContainer*) colorAttachmentInfos[i].texture)->vulkanTexture; - - renderTarget = VULKAN_INTERNAL_FetchRenderTarget( - renderer, - texture, - colorAttachmentInfos[i].depth, - colorAttachmentInfos[i].layer, - colorAttachmentInfos[i].level - ); - - imageViewAttachments[attachmentCount] = - renderTarget->view; - - attachmentCount += 1; - - if (texture->msaaTex != NULL) - { - renderTarget = VULKAN_INTERNAL_FetchRenderTarget( - renderer, - texture->msaaTex, - colorAttachmentInfos[i].depth, - colorAttachmentInfos[i].layer, - colorAttachmentInfos[i].level - ); - - imageViewAttachments[attachmentCount] = - renderTarget->view; - - attachmentCount += 1; - } - } - - if (depthStencilAttachmentInfo != NULL) - { - texture = ((VulkanTextureContainer*) depthStencilAttachmentInfo->texture)->vulkanTexture; - renderTarget = VULKAN_INTERNAL_FetchRenderTarget( - renderer, - texture, - depthStencilAttachmentInfo->depth, - depthStencilAttachmentInfo->layer, - depthStencilAttachmentInfo->level - ); - - imageViewAttachments[attachmentCount] = renderTarget->view; - - attachmentCount += 1; - } - - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.pNext = NULL; - framebufferInfo.flags = 0; - framebufferInfo.renderPass = renderPass; - framebufferInfo.attachmentCount = attachmentCount; - framebufferInfo.pAttachments = imageViewAttachments; - framebufferInfo.width = hash.width; - framebufferInfo.height = hash.height; - framebufferInfo.layers = 1; - - result = renderer->vkCreateFramebuffer( - renderer->logicalDevice, - &framebufferInfo, - NULL, - &vulkanFramebuffer->framebuffer - ); - - if (result == VK_SUCCESS) - { - SDL_LockMutex(renderer->framebufferFetchLock); - - FramebufferHashArray_Insert( - &renderer->framebufferHashArray, - hash, - vulkanFramebuffer - ); - - SDL_UnlockMutex(renderer->framebufferFetchLock); - } - else - { - LogVulkanResultAsError("vkCreateFramebuffer", result); - SDL_free(vulkanFramebuffer); - vulkanFramebuffer = NULL; - } - - return vulkanFramebuffer; -} - -static void VULKAN_INTERNAL_SetCurrentViewport( - VulkanCommandBuffer *commandBuffer, - Refresh_Viewport *viewport -) { - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - - vulkanCommandBuffer->currentViewport.x = viewport->x; - vulkanCommandBuffer->currentViewport.y = viewport->y; - vulkanCommandBuffer->currentViewport.width = viewport->w; - vulkanCommandBuffer->currentViewport.height = viewport->h; - vulkanCommandBuffer->currentViewport.minDepth = viewport->minDepth; - vulkanCommandBuffer->currentViewport.maxDepth = viewport->maxDepth; -} - -static void VULKAN_SetViewport( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Viewport *viewport -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - - VULKAN_INTERNAL_SetCurrentViewport( - vulkanCommandBuffer, - viewport - ); - - renderer->vkCmdSetViewport( - vulkanCommandBuffer->commandBuffer, - 0, - 1, - &vulkanCommandBuffer->currentViewport - ); -} - -static void VULKAN_INTERNAL_SetCurrentScissor( - VulkanCommandBuffer *vulkanCommandBuffer, - Refresh_Rect *scissor -) { - vulkanCommandBuffer->currentScissor.offset.x = scissor->x; - vulkanCommandBuffer->currentScissor.offset.y = scissor->y; - vulkanCommandBuffer->currentScissor.extent.width = scissor->w; - vulkanCommandBuffer->currentScissor.extent.height = scissor->h; -} - -static void VULKAN_SetScissor( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Rect *scissor -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - - VULKAN_INTERNAL_SetCurrentScissor( - vulkanCommandBuffer, - scissor - ); - - renderer->vkCmdSetScissor( - vulkanCommandBuffer->commandBuffer, - 0, - 1, - &vulkanCommandBuffer->currentScissor - ); -} - -static void VULKAN_BeginRenderPass( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_ColorAttachmentInfo *colorAttachmentInfos, - uint32_t colorAttachmentCount, - Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VkRenderPass renderPass; - VulkanFramebuffer *framebuffer; - - VulkanTexture *texture; - uint32_t w, h; - VkClearValue *clearValues; - uint32_t clearCount = colorAttachmentCount; - uint32_t multisampleAttachmentCount = 0; - uint32_t totalColorAttachmentCount = 0; - uint32_t i; - VkImageAspectFlags depthAspectFlags; - Refresh_Viewport defaultViewport; - Refresh_Rect defaultScissor; - uint32_t framebufferWidth = UINT32_MAX; - uint32_t framebufferHeight = UINT32_MAX; - - /* The framebuffer cannot be larger than the smallest attachment. */ - - for (i = 0; i < colorAttachmentCount; i += 1) - { - texture = ((VulkanTextureContainer*) colorAttachmentInfos[i].texture)->vulkanTexture; - w = texture->dimensions.width >> colorAttachmentInfos[i].level; - h = texture->dimensions.height >> colorAttachmentInfos[i].level; - - if (w < framebufferWidth) - { - framebufferWidth = w; - } - - if (h < framebufferHeight) - { - framebufferHeight = h; - } - } - - if (depthStencilAttachmentInfo != NULL) - { - texture = ((VulkanTextureContainer*) depthStencilAttachmentInfo->texture)->vulkanTexture; - w = texture->dimensions.width >> depthStencilAttachmentInfo->level; - h = texture->dimensions.height >> depthStencilAttachmentInfo->level; - - if (w < framebufferWidth) - { - framebufferWidth = w; - } - - if (h < framebufferHeight) - { - framebufferHeight = h; - } - } - - /* Fetch required render objects */ - - renderPass = VULKAN_INTERNAL_FetchRenderPass( - renderer, - vulkanCommandBuffer, - colorAttachmentInfos, - colorAttachmentCount, - depthStencilAttachmentInfo - ); - - framebuffer = VULKAN_INTERNAL_FetchFramebuffer( - renderer, - renderPass, - colorAttachmentInfos, - colorAttachmentCount, - depthStencilAttachmentInfo, - framebufferWidth, - framebufferHeight - ); - - VULKAN_INTERNAL_TrackFramebuffer(renderer, vulkanCommandBuffer, framebuffer); - - /* Layout transitions */ - - for (i = 0; i < colorAttachmentCount; i += 1) - { - texture = ((VulkanTextureContainer*) colorAttachmentInfos[i].texture)->vulkanTexture; - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_COLOR_ATTACHMENT_READ_WRITE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - texture->layerCount, - 0, - texture->levelCount, - 0, - texture->image, - &texture->resourceAccessType - ); - - if (texture->msaaTex != NULL) - { - clearCount += 1; - multisampleAttachmentCount += 1; - } - - VULKAN_INTERNAL_TrackTexture(renderer, vulkanCommandBuffer, texture); - } - - if (depthStencilAttachmentInfo != NULL) - { - texture = ((VulkanTextureContainer*) depthStencilAttachmentInfo->texture)->vulkanTexture; - depthAspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT; - - if (IsStencilFormat(texture->format)) - { - depthAspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_WRITE, - depthAspectFlags, - 0, - texture->layerCount, - 0, - texture->levelCount, - 0, - texture->image, - &texture->resourceAccessType - ); - - clearCount += 1; - - VULKAN_INTERNAL_TrackTexture(renderer, vulkanCommandBuffer, texture); - } - - /* Set clear values */ - - clearValues = SDL_stack_alloc(VkClearValue, clearCount); - - totalColorAttachmentCount = colorAttachmentCount + multisampleAttachmentCount; - - for (i = 0; i < totalColorAttachmentCount; i += 1) - { - clearValues[i].color.float32[0] = colorAttachmentInfos[i].clearColor.x; - clearValues[i].color.float32[1] = colorAttachmentInfos[i].clearColor.y; - clearValues[i].color.float32[2] = colorAttachmentInfos[i].clearColor.z; - clearValues[i].color.float32[3] = colorAttachmentInfos[i].clearColor.w; - - texture = ((VulkanTextureContainer*) colorAttachmentInfos[i].texture)->vulkanTexture; - if (texture->msaaTex != NULL) - { - clearValues[i+1].color.float32[0] = colorAttachmentInfos[i].clearColor.x; - clearValues[i+1].color.float32[1] = colorAttachmentInfos[i].clearColor.y; - clearValues[i+1].color.float32[2] = colorAttachmentInfos[i].clearColor.z; - clearValues[i+1].color.float32[3] = colorAttachmentInfos[i].clearColor.w; - i += 1; - } - } - - if (depthStencilAttachmentInfo != NULL) - { - clearValues[totalColorAttachmentCount].depthStencil.depth = - depthStencilAttachmentInfo->depthStencilClearValue.depth; - clearValues[totalColorAttachmentCount].depthStencil.stencil = - depthStencilAttachmentInfo->depthStencilClearValue.stencil; - } - - VkRenderPassBeginInfo renderPassBeginInfo; - renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassBeginInfo.pNext = NULL; - renderPassBeginInfo.renderPass = renderPass; - renderPassBeginInfo.framebuffer = framebuffer->framebuffer; - renderPassBeginInfo.pClearValues = clearValues; - renderPassBeginInfo.clearValueCount = clearCount; - renderPassBeginInfo.renderArea.extent.width = framebufferWidth; - renderPassBeginInfo.renderArea.extent.height = framebufferHeight; - renderPassBeginInfo.renderArea.offset.x = 0; - renderPassBeginInfo.renderArea.offset.y = 0; - - renderer->vkCmdBeginRenderPass( - vulkanCommandBuffer->commandBuffer, - &renderPassBeginInfo, - VK_SUBPASS_CONTENTS_INLINE - ); - - SDL_stack_free(clearValues); - - for (i = 0; i < colorAttachmentCount; i += 1) - { - vulkanCommandBuffer->renderPassColorTargetTextures[i] = - ((VulkanTextureContainer*) colorAttachmentInfos[i].texture)->vulkanTexture; - } - vulkanCommandBuffer->renderPassColorTargetCount = colorAttachmentCount; - - if (depthStencilAttachmentInfo != NULL) - { - vulkanCommandBuffer->renderPassDepthTexture = ((VulkanTextureContainer*) depthStencilAttachmentInfo->texture)->vulkanTexture; - } - - /* Set sensible default viewport state */ - - defaultViewport.x = 0; - defaultViewport.y = 0; - defaultViewport.w = framebufferWidth; - defaultViewport.h = framebufferHeight; - defaultViewport.minDepth = 0; - defaultViewport.maxDepth = 1; - - VULKAN_INTERNAL_SetCurrentViewport( - vulkanCommandBuffer, - &defaultViewport - ); - - defaultScissor.x = 0; - defaultScissor.y = 0; - defaultScissor.w = framebufferWidth; - defaultScissor.h = framebufferHeight; - - VULKAN_INTERNAL_SetCurrentScissor( - vulkanCommandBuffer, - &defaultScissor - ); -} - -static void VULKAN_EndRenderPass( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanTexture *currentTexture; - uint32_t i; - - renderer->vkCmdEndRenderPass( - vulkanCommandBuffer->commandBuffer - ); - - if ( vulkanCommandBuffer->vertexUniformBuffer != renderer->dummyVertexUniformBuffer && - vulkanCommandBuffer->vertexUniformBuffer != NULL - ) { - VULKAN_INTERNAL_BindUniformBuffer( - renderer, - vulkanCommandBuffer, - vulkanCommandBuffer->vertexUniformBuffer - ); - } - vulkanCommandBuffer->vertexUniformBuffer = NULL; - - if ( vulkanCommandBuffer->fragmentUniformBuffer != renderer->dummyFragmentUniformBuffer && - vulkanCommandBuffer->fragmentUniformBuffer != NULL - ) { - VULKAN_INTERNAL_BindUniformBuffer( - renderer, - vulkanCommandBuffer, - vulkanCommandBuffer->fragmentUniformBuffer - ); - } - vulkanCommandBuffer->fragmentUniformBuffer = NULL; - - /* If the render targets can be sampled, transition them to sample layout */ - for (i = 0; i < vulkanCommandBuffer->renderPassColorTargetCount; i += 1) - { - currentTexture = vulkanCommandBuffer->renderPassColorTargetTextures[i]; - - if (currentTexture->usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) - { - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_ANY_SHADER_READ_SAMPLED_IMAGE, - currentTexture->aspectFlags, - 0, - currentTexture->layerCount, - 0, - currentTexture->levelCount, - 0, - currentTexture->image, - ¤tTexture->resourceAccessType - ); - } - else if (currentTexture->usageFlags & VK_IMAGE_USAGE_STORAGE_BIT) - { - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_COMPUTE_SHADER_STORAGE_IMAGE_READ_WRITE, - currentTexture->aspectFlags, - 0, - currentTexture->layerCount, - 0, - currentTexture->levelCount, - 0, - currentTexture->image, - ¤tTexture->resourceAccessType - ); - } - } - vulkanCommandBuffer->renderPassColorTargetCount = 0; - - if (vulkanCommandBuffer->renderPassDepthTexture != NULL) - { - currentTexture = vulkanCommandBuffer->renderPassDepthTexture; - - if (currentTexture->usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) - { - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_ANY_SHADER_READ_SAMPLED_IMAGE, - currentTexture->aspectFlags, - 0, - currentTexture->layerCount, - 0, - currentTexture->levelCount, - 0, - currentTexture->image, - ¤tTexture->resourceAccessType - ); - } - } - vulkanCommandBuffer->renderPassDepthTexture = NULL; - - vulkanCommandBuffer->currentGraphicsPipeline = NULL; -} - -static void VULKAN_BindGraphicsPipeline( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_GraphicsPipeline *graphicsPipeline -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanGraphicsPipeline* pipeline = (VulkanGraphicsPipeline*) graphicsPipeline; - - if ( vulkanCommandBuffer->vertexUniformBuffer != renderer->dummyVertexUniformBuffer && - vulkanCommandBuffer->vertexUniformBuffer != NULL - ) { - VULKAN_INTERNAL_BindUniformBuffer( - renderer, - vulkanCommandBuffer, - vulkanCommandBuffer->vertexUniformBuffer - ); - } - - if (pipeline->vertexUniformBlockSize == 0) - { - vulkanCommandBuffer->vertexUniformBuffer = renderer->dummyVertexUniformBuffer; - } - else - { - vulkanCommandBuffer->vertexUniformBuffer = VULKAN_INTERNAL_AcquireUniformBufferFromPool( - renderer, - renderer->vertexUniformBufferPool, - pipeline->vertexUniformBlockSize - ); - } - - if ( vulkanCommandBuffer->fragmentUniformBuffer != renderer->dummyFragmentUniformBuffer && - vulkanCommandBuffer->fragmentUniformBuffer != NULL - ) { - VULKAN_INTERNAL_BindUniformBuffer( - renderer, - vulkanCommandBuffer, - vulkanCommandBuffer->fragmentUniformBuffer - ); - } - - if (pipeline->fragmentUniformBlockSize == 0) - { - vulkanCommandBuffer->fragmentUniformBuffer = renderer->dummyFragmentUniformBuffer; - } - else - { - vulkanCommandBuffer->fragmentUniformBuffer = VULKAN_INTERNAL_AcquireUniformBufferFromPool( - renderer, - renderer->fragmentUniformBufferPool, - pipeline->fragmentUniformBlockSize - ); - } - - /* bind dummy sets if necessary */ - if (pipeline->pipelineLayout->vertexSamplerDescriptorSetCache == NULL) - { - vulkanCommandBuffer->vertexSamplerDescriptorSet = renderer->emptyVertexSamplerDescriptorSet; - } - - if (pipeline->pipelineLayout->fragmentSamplerDescriptorSetCache == NULL) - { - vulkanCommandBuffer->fragmentSamplerDescriptorSet = renderer->emptyFragmentSamplerDescriptorSet; - } - - renderer->vkCmdBindPipeline( - vulkanCommandBuffer->commandBuffer, - VK_PIPELINE_BIND_POINT_GRAPHICS, - pipeline->pipeline - ); - - vulkanCommandBuffer->currentGraphicsPipeline = pipeline; - - VULKAN_INTERNAL_TrackGraphicsPipeline(renderer, vulkanCommandBuffer, pipeline); - - renderer->vkCmdSetViewport( - vulkanCommandBuffer->commandBuffer, - 0, - 1, - &vulkanCommandBuffer->currentViewport - ); - - renderer->vkCmdSetScissor( - vulkanCommandBuffer->commandBuffer, - 0, - 1, - &vulkanCommandBuffer->currentScissor - ); -} - -static void VULKAN_BindVertexBuffers( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - uint32_t firstBinding, - uint32_t bindingCount, - Refresh_Buffer **pBuffers, - uint64_t *pOffsets -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanBuffer *currentVulkanBuffer; - VkBuffer *buffers = SDL_stack_alloc(VkBuffer, bindingCount); - uint32_t i; - - for (i = 0; i < bindingCount; i += 1) - { - currentVulkanBuffer = ((VulkanBufferContainer*) pBuffers[i])->vulkanBuffer; - buffers[i] = currentVulkanBuffer->buffer; - VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, currentVulkanBuffer); - } - - renderer->vkCmdBindVertexBuffers( - vulkanCommandBuffer->commandBuffer, - firstBinding, - bindingCount, - buffers, - pOffsets - ); - - SDL_stack_free(buffers); -} - -static void VULKAN_BindIndexBuffer( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer *buffer, - uint64_t offset, - Refresh_IndexElementSize indexElementSize -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanBuffer* vulkanBuffer = ((VulkanBufferContainer*) buffer)->vulkanBuffer; - - VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, vulkanBuffer); - - renderer->vkCmdBindIndexBuffer( - vulkanCommandBuffer->commandBuffer, - vulkanBuffer->buffer, - offset, - RefreshToVK_IndexType[indexElementSize] - ); -} - -static void VULKAN_BindComputePipeline( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_ComputePipeline *computePipeline -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanComputePipeline *vulkanComputePipeline = (VulkanComputePipeline*) computePipeline; - - /* bind dummy sets */ - if (vulkanComputePipeline->pipelineLayout->bufferDescriptorSetCache == NULL) - { - vulkanCommandBuffer->bufferDescriptorSet = renderer->emptyComputeBufferDescriptorSet; - } - - if (vulkanComputePipeline->pipelineLayout->imageDescriptorSetCache == NULL) - { - vulkanCommandBuffer->imageDescriptorSet = renderer->emptyComputeImageDescriptorSet; - } - - if ( vulkanCommandBuffer->computeUniformBuffer != renderer->dummyComputeUniformBuffer && - vulkanCommandBuffer->computeUniformBuffer != NULL - ) { - VULKAN_INTERNAL_BindUniformBuffer( - renderer, - vulkanCommandBuffer, - vulkanCommandBuffer->computeUniformBuffer - ); - } - renderer->vkCmdBindPipeline( - vulkanCommandBuffer->commandBuffer, - VK_PIPELINE_BIND_POINT_COMPUTE, - vulkanComputePipeline->pipeline - ); - - vulkanCommandBuffer->currentComputePipeline = vulkanComputePipeline; - - if (vulkanComputePipeline->uniformBlockSize == 0) - { - vulkanCommandBuffer->computeUniformBuffer = renderer->dummyComputeUniformBuffer; - } - else - { - vulkanCommandBuffer->computeUniformBuffer = VULKAN_INTERNAL_AcquireUniformBufferFromPool( - renderer, - renderer->computeUniformBufferPool, - vulkanComputePipeline->uniformBlockSize - ); - } - - VULKAN_INTERNAL_TrackComputePipeline(renderer, vulkanCommandBuffer, vulkanComputePipeline); -} - -static void VULKAN_BindComputeBuffers( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Buffer **pBuffers -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanComputePipeline *computePipeline = vulkanCommandBuffer->currentComputePipeline; - - VulkanBuffer *currentVulkanBuffer; - VkDescriptorBufferInfo descriptorBufferInfos[MAX_BUFFER_BINDINGS]; - uint32_t i; - - if (computePipeline->pipelineLayout->bufferDescriptorSetCache == NULL) - { - return; - } - - for (i = 0; i < computePipeline->pipelineLayout->bufferDescriptorSetCache->bindingCount; i += 1) - { - currentVulkanBuffer = ((VulkanBufferContainer*) pBuffers[i])->vulkanBuffer; - - descriptorBufferInfos[i].buffer = currentVulkanBuffer->buffer; - descriptorBufferInfos[i].offset = 0; - descriptorBufferInfos[i].range = currentVulkanBuffer->size; - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_COMPUTE_SHADER_BUFFER_READ_WRITE, - currentVulkanBuffer - ); - - VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, currentVulkanBuffer); - } - - vulkanCommandBuffer->bufferDescriptorSet = - VULKAN_INTERNAL_FetchDescriptorSet( - renderer, - vulkanCommandBuffer, - computePipeline->pipelineLayout->bufferDescriptorSetCache, - NULL, - descriptorBufferInfos - ); - - if (vulkanCommandBuffer->boundComputeBufferCount == vulkanCommandBuffer->boundComputeBufferCapacity) - { - vulkanCommandBuffer->boundComputeBufferCapacity *= 2; - vulkanCommandBuffer->boundComputeBuffers = SDL_realloc( - vulkanCommandBuffer->boundComputeBuffers, - vulkanCommandBuffer->boundComputeBufferCapacity * sizeof(VulkanBuffer*) - ); - } - - vulkanCommandBuffer->boundComputeBuffers[vulkanCommandBuffer->boundComputeBufferCount] = currentVulkanBuffer; - vulkanCommandBuffer->boundComputeBufferCount += 1; -} - -static void VULKAN_BindComputeTextures( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - Refresh_Texture **pTextures -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - VulkanComputePipeline *computePipeline = vulkanCommandBuffer->currentComputePipeline; - - VulkanTexture *currentTexture; - VkDescriptorImageInfo descriptorImageInfos[MAX_TEXTURE_SAMPLERS]; - uint32_t i; - - if (computePipeline->pipelineLayout->imageDescriptorSetCache == NULL) - { - return; - } - - for (i = 0; i < computePipeline->pipelineLayout->imageDescriptorSetCache->bindingCount; i += 1) - { - currentTexture = ((VulkanTextureContainer*) pTextures[i])->vulkanTexture; - descriptorImageInfos[i].imageView = currentTexture->view; - descriptorImageInfos[i].sampler = VK_NULL_HANDLE; - descriptorImageInfos[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_COMPUTE_SHADER_STORAGE_IMAGE_READ_WRITE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - currentTexture->layerCount, - 0, - currentTexture->levelCount, - 0, - currentTexture->image, - ¤tTexture->resourceAccessType - ); - - VULKAN_INTERNAL_TrackTexture(renderer, vulkanCommandBuffer, currentTexture); - - if (vulkanCommandBuffer->boundComputeTextureCount == vulkanCommandBuffer->boundComputeTextureCapacity) - { - vulkanCommandBuffer->boundComputeTextureCapacity *= 2; - vulkanCommandBuffer->boundComputeTextures = SDL_realloc( - vulkanCommandBuffer->boundComputeTextures, - vulkanCommandBuffer->boundComputeTextureCapacity * sizeof(VulkanTexture *) - ); - } - - vulkanCommandBuffer->boundComputeTextures[i] = currentTexture; - vulkanCommandBuffer->boundComputeTextureCount += 1; - } - - vulkanCommandBuffer->imageDescriptorSet = - VULKAN_INTERNAL_FetchDescriptorSet( - renderer, - vulkanCommandBuffer, - computePipeline->pipelineLayout->imageDescriptorSetCache, - descriptorImageInfos, - NULL - ); -} - -static void VULKAN_INTERNAL_AllocateCommandBuffers( - VulkanRenderer *renderer, - VulkanCommandPool *vulkanCommandPool, - uint32_t allocateCount -) { - VkCommandBufferAllocateInfo allocateInfo; - VkResult vulkanResult; - uint32_t i; - VkCommandBuffer *commandBuffers = SDL_stack_alloc(VkCommandBuffer, allocateCount); - VulkanCommandBuffer *commandBuffer; - - vulkanCommandPool->inactiveCommandBufferCapacity += allocateCount; - - vulkanCommandPool->inactiveCommandBuffers = SDL_realloc( - vulkanCommandPool->inactiveCommandBuffers, - sizeof(VulkanCommandBuffer*) * - vulkanCommandPool->inactiveCommandBufferCapacity - ); - - allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocateInfo.pNext = NULL; - allocateInfo.commandPool = vulkanCommandPool->commandPool; - allocateInfo.commandBufferCount = allocateCount; - allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - - vulkanResult = renderer->vkAllocateCommandBuffers( - renderer->logicalDevice, - &allocateInfo, - commandBuffers - ); - - if (vulkanResult != VK_SUCCESS) - { - LogVulkanResultAsError("vkAllocateCommandBuffers", vulkanResult); - SDL_stack_free(commandBuffers); - return; - } - - for (i = 0; i < allocateCount; i += 1) - { - commandBuffer = SDL_malloc(sizeof(VulkanCommandBuffer)); - commandBuffer->commandPool = vulkanCommandPool; - commandBuffer->commandBuffer = commandBuffers[i]; - - commandBuffer->inFlightFence = VK_NULL_HANDLE; - commandBuffer->renderPassDepthTexture = NULL; - - /* Presentation tracking */ - - commandBuffer->presentDataCapacity = 1; - commandBuffer->presentDataCount = 0; - commandBuffer->presentDatas = SDL_malloc( - commandBuffer->presentDataCapacity * sizeof(VkPresentInfoKHR) - ); - - commandBuffer->waitSemaphoreCapacity = 1; - commandBuffer->waitSemaphoreCount = 0; - commandBuffer->waitSemaphores = SDL_malloc( - commandBuffer->waitSemaphoreCapacity * sizeof(VkSemaphore) - ); - - commandBuffer->signalSemaphoreCapacity = 1; - commandBuffer->signalSemaphoreCount = 0; - commandBuffer->signalSemaphores = SDL_malloc( - commandBuffer->signalSemaphoreCapacity * sizeof(VkSemaphore) - ); - - /* Transfer buffer tracking */ - - commandBuffer->transferBufferCapacity = 4; - commandBuffer->transferBufferCount = 0; - commandBuffer->transferBuffers = SDL_malloc( - commandBuffer->transferBufferCapacity * sizeof(VulkanTransferBuffer*) - ); - - /* Bound buffer tracking */ - - commandBuffer->boundUniformBufferCapacity = 16; - commandBuffer->boundUniformBufferCount = 0; - commandBuffer->boundUniformBuffers = SDL_malloc( - commandBuffer->boundUniformBufferCapacity * sizeof(VulkanUniformBuffer*) - ); - - /* Descriptor set tracking */ - - commandBuffer->boundDescriptorSetDataCapacity = 16; - commandBuffer->boundDescriptorSetDataCount = 0; - commandBuffer->boundDescriptorSetDatas = SDL_malloc( - commandBuffer->boundDescriptorSetDataCapacity * sizeof(DescriptorSetData) - ); - - /* Bound compute resource tracking */ - - commandBuffer->boundComputeBufferCapacity = 16; - commandBuffer->boundComputeBufferCount = 0; - commandBuffer->boundComputeBuffers = SDL_malloc( - commandBuffer->boundComputeBufferCapacity * sizeof(VulkanBuffer*) - ); - - commandBuffer->boundComputeTextureCapacity = 16; - commandBuffer->boundComputeTextureCount = 0; - commandBuffer->boundComputeTextures = SDL_malloc( - commandBuffer->boundComputeTextureCapacity * sizeof(VulkanTexture*) - ); - - /* Resource tracking */ - - commandBuffer->usedBufferCapacity = 4; - commandBuffer->usedBufferCount = 0; - commandBuffer->usedBuffers = SDL_malloc( - commandBuffer->usedBufferCapacity * sizeof(VulkanBuffer*) - ); - - commandBuffer->usedTextureCapacity = 4; - commandBuffer->usedTextureCount = 0; - commandBuffer->usedTextures = SDL_malloc( - commandBuffer->usedTextureCapacity * sizeof(VulkanTexture*) - ); - - commandBuffer->usedSamplerCapacity = 4; - commandBuffer->usedSamplerCount = 0; - commandBuffer->usedSamplers = SDL_malloc( - commandBuffer->usedSamplerCapacity * sizeof(VulkanSampler*) - ); - - commandBuffer->usedGraphicsPipelineCapacity = 4; - commandBuffer->usedGraphicsPipelineCount = 0; - commandBuffer->usedGraphicsPipelines = SDL_malloc( - commandBuffer->usedGraphicsPipelineCapacity * sizeof(VulkanGraphicsPipeline*) - ); - - commandBuffer->usedComputePipelineCapacity = 4; - commandBuffer->usedComputePipelineCount = 0; - commandBuffer->usedComputePipelines = SDL_malloc( - commandBuffer->usedComputePipelineCapacity * sizeof(VulkanComputePipeline*) - ); - - commandBuffer->usedFramebufferCapacity = 4; - commandBuffer->usedFramebufferCount = 0; - commandBuffer->usedFramebuffers = SDL_malloc( - commandBuffer->usedFramebufferCapacity * sizeof(VulkanFramebuffer*) - ); - - vulkanCommandPool->inactiveCommandBuffers[ - vulkanCommandPool->inactiveCommandBufferCount - ] = commandBuffer; - vulkanCommandPool->inactiveCommandBufferCount += 1; - } - - SDL_stack_free(commandBuffers); -} - -static VulkanCommandPool* VULKAN_INTERNAL_FetchCommandPool( - VulkanRenderer *renderer, - SDL_threadID threadID -) { - VulkanCommandPool *vulkanCommandPool; - VkCommandPoolCreateInfo commandPoolCreateInfo; - VkResult vulkanResult; - CommandPoolHash commandPoolHash; - - commandPoolHash.threadID = threadID; - - vulkanCommandPool = CommandPoolHashTable_Fetch( - &renderer->commandPoolHashTable, - commandPoolHash - ); - - if (vulkanCommandPool != NULL) - { - return vulkanCommandPool; - } - - vulkanCommandPool = (VulkanCommandPool*) SDL_malloc(sizeof(VulkanCommandPool)); - - commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - commandPoolCreateInfo.pNext = NULL; - commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - commandPoolCreateInfo.queueFamilyIndex = renderer->queueFamilyIndex; - - vulkanResult = renderer->vkCreateCommandPool( - renderer->logicalDevice, - &commandPoolCreateInfo, - NULL, - &vulkanCommandPool->commandPool - ); - - if (vulkanResult != VK_SUCCESS) - { - Refresh_LogError("Failed to create command pool!"); - LogVulkanResultAsError("vkCreateCommandPool", vulkanResult); - return NULL; - } - - vulkanCommandPool->threadID = threadID; - - vulkanCommandPool->inactiveCommandBufferCapacity = 0; - vulkanCommandPool->inactiveCommandBufferCount = 0; - vulkanCommandPool->inactiveCommandBuffers = NULL; - - VULKAN_INTERNAL_AllocateCommandBuffers( - renderer, - vulkanCommandPool, - 2 - ); - - CommandPoolHashTable_Insert( - &renderer->commandPoolHashTable, - commandPoolHash, - vulkanCommandPool - ); - - return vulkanCommandPool; -} - -static VulkanCommandBuffer* VULKAN_INTERNAL_GetInactiveCommandBufferFromPool( - VulkanRenderer *renderer, - SDL_threadID threadID -) { - VulkanCommandPool *commandPool = - VULKAN_INTERNAL_FetchCommandPool(renderer, threadID); - VulkanCommandBuffer *commandBuffer; - - if (commandPool->inactiveCommandBufferCount == 0) - { - VULKAN_INTERNAL_AllocateCommandBuffers( - renderer, - commandPool, - commandPool->inactiveCommandBufferCapacity - ); - } - - commandBuffer = commandPool->inactiveCommandBuffers[commandPool->inactiveCommandBufferCount - 1]; - commandPool->inactiveCommandBufferCount -= 1; - - return commandBuffer; -} - -static Refresh_CommandBuffer* VULKAN_AcquireCommandBuffer( - Refresh_Renderer *driverData -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VkResult result; - - SDL_threadID threadID = SDL_ThreadID(); - - SDL_LockMutex(renderer->acquireCommandBufferLock); - - VulkanCommandBuffer *commandBuffer = - VULKAN_INTERNAL_GetInactiveCommandBufferFromPool(renderer, threadID); - - SDL_UnlockMutex(renderer->acquireCommandBufferLock); - - /* Reset state */ - - commandBuffer->currentComputePipeline = NULL; - commandBuffer->currentGraphicsPipeline = NULL; - - commandBuffer->vertexUniformBuffer = NULL; - commandBuffer->fragmentUniformBuffer = NULL; - commandBuffer->computeUniformBuffer = NULL; - - commandBuffer->renderPassColorTargetCount = 0; - - /* Reset the command buffer here to avoid resets being called - * from a separate thread than where the command buffer was acquired - */ - result = renderer->vkResetCommandBuffer( - commandBuffer->commandBuffer, - VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT - ); - - if (result != VK_SUCCESS) - { - LogVulkanResultAsError("vkResetCommandBuffer", result); - } - - VULKAN_INTERNAL_BeginCommandBuffer(renderer, commandBuffer); - - return (Refresh_CommandBuffer*) commandBuffer; -} - -static WindowData* VULKAN_INTERNAL_FetchWindowData( - void *windowHandle -) { - return (WindowData*) SDL_GetWindowData(windowHandle, WINDOW_DATA); -} - -static uint8_t VULKAN_ClaimWindow( - Refresh_Renderer *driverData, - void *windowHandle, - Refresh_PresentMode presentMode -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(windowHandle); - - if (windowData == NULL) - { - windowData = SDL_malloc(sizeof(WindowData)); - windowData->windowHandle = windowHandle; - windowData->preferredPresentMode = presentMode; - - if (VULKAN_INTERNAL_CreateSwapchain(renderer, windowData)) - { - SDL_SetWindowData((SDL_Window*) windowHandle, WINDOW_DATA, windowData); - - if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) - { - renderer->claimedWindowCapacity *= 2; - renderer->claimedWindows = SDL_realloc( - renderer->claimedWindows, - renderer->claimedWindowCapacity * sizeof(WindowData*) - ); - } - - renderer->claimedWindows[renderer->claimedWindowCount] = windowData; - renderer->claimedWindowCount += 1; - - return 1; - } - else - { - Refresh_LogError("Could not create swapchain, failed to claim window!"); - SDL_free(windowData); - return 0; - } - } - else - { - Refresh_LogWarn("Window already claimed!"); - return 0; - } -} - -static void VULKAN_UnclaimWindow( - Refresh_Renderer *driverData, - void *windowHandle -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(windowHandle); - uint32_t i; - - if (windowData == NULL) - { - return; - } - - if (windowData->swapchainData != NULL) - { - VULKAN_Wait(driverData); - VULKAN_INTERNAL_DestroySwapchain( - (VulkanRenderer*) driverData, - windowData - ); - } - - for (i = 0; i < renderer->claimedWindowCount; i += 1) - { - if (renderer->claimedWindows[i]->windowHandle == windowHandle) - { - renderer->claimedWindows[i] = renderer->claimedWindows[renderer->claimedWindowCount - 1]; - renderer->claimedWindowCount -= 1; - break; - } - } - - SDL_free(windowData); - SDL_SetWindowData((SDL_Window*) windowHandle, WINDOW_DATA, NULL); -} - -static Refresh_Texture* VULKAN_AcquireSwapchainTexture( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer, - void *windowHandle, - uint32_t *pWidth, - uint32_t *pHeight -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - uint32_t swapchainImageIndex; - WindowData *windowData; - VulkanSwapchainData *swapchainData; - VkResult acquireResult = VK_SUCCESS; - VulkanTextureContainer *swapchainTextureContainer = NULL; - VulkanPresentData *presentData; - - windowData = VULKAN_INTERNAL_FetchWindowData(windowHandle); - if (windowData == NULL) - { - return NULL; - } - - swapchainData = windowData->swapchainData; - - /* Window is claimed but swapchain is invalid! */ - if (swapchainData == NULL) - { - if (SDL_GetWindowFlags(windowHandle) & SDL_WINDOW_MINIMIZED) - { - /* Window is minimized, don't bother */ - return NULL; - } - - /* Let's try to recreate */ - VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData); - swapchainData = windowData->swapchainData; - - if (swapchainData == NULL) - { - Refresh_LogWarn("Failed to recreate swapchain!"); - return NULL; - } - } - - acquireResult = renderer->vkAcquireNextImageKHR( - renderer->logicalDevice, - swapchainData->swapchain, - UINT64_MAX, - swapchainData->imageAvailableSemaphore, - VK_NULL_HANDLE, - &swapchainImageIndex - ); - - /* Acquisition is invalid, let's try to recreate */ - if (acquireResult != VK_SUCCESS && acquireResult != VK_SUBOPTIMAL_KHR) - { - VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData); - swapchainData = windowData->swapchainData; - - if (swapchainData == NULL) - { - Refresh_LogWarn("Failed to recreate swapchain!"); - return NULL; - } - - acquireResult = renderer->vkAcquireNextImageKHR( - renderer->logicalDevice, - swapchainData->swapchain, - UINT64_MAX, - swapchainData->imageAvailableSemaphore, - VK_NULL_HANDLE, - &swapchainImageIndex - ); - - if (acquireResult != VK_SUCCESS && acquireResult != VK_SUBOPTIMAL_KHR) - { - Refresh_LogWarn("Failed to acquire swapchain texture!"); - return NULL; - } - } - - swapchainTextureContainer = &swapchainData->textureContainers[swapchainImageIndex]; - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_COLOR_ATTACHMENT_WRITE, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - 1, - 0, - 1, - 0, - swapchainTextureContainer->vulkanTexture->image, - &swapchainTextureContainer->vulkanTexture->resourceAccessType - ); - - /* Set up present struct */ - - if (vulkanCommandBuffer->presentDataCount == vulkanCommandBuffer->presentDataCapacity) - { - vulkanCommandBuffer->presentDataCapacity += 1; - vulkanCommandBuffer->presentDatas = SDL_realloc( - vulkanCommandBuffer->presentDatas, - vulkanCommandBuffer->presentDataCapacity * sizeof(VkPresentInfoKHR) - ); - } - - presentData = &vulkanCommandBuffer->presentDatas[vulkanCommandBuffer->presentDataCount]; - vulkanCommandBuffer->presentDataCount += 1; - - presentData->windowData = windowData; - presentData->swapchainImageIndex = swapchainImageIndex; - - /* Set up present semaphores */ - - if (vulkanCommandBuffer->waitSemaphoreCount == vulkanCommandBuffer->waitSemaphoreCapacity) - { - vulkanCommandBuffer->waitSemaphoreCapacity += 1; - vulkanCommandBuffer->waitSemaphores = SDL_realloc( - vulkanCommandBuffer->waitSemaphores, - vulkanCommandBuffer->waitSemaphoreCapacity * sizeof(VkSemaphore) - ); - } - - vulkanCommandBuffer->waitSemaphores[vulkanCommandBuffer->waitSemaphoreCount] = swapchainData->imageAvailableSemaphore; - vulkanCommandBuffer->waitSemaphoreCount += 1; - - if (vulkanCommandBuffer->signalSemaphoreCount == vulkanCommandBuffer->signalSemaphoreCapacity) - { - vulkanCommandBuffer->signalSemaphoreCapacity += 1; - vulkanCommandBuffer->signalSemaphores = SDL_realloc( - vulkanCommandBuffer->signalSemaphores, - vulkanCommandBuffer->signalSemaphoreCapacity * sizeof(VkSemaphore) - ); - } - - vulkanCommandBuffer->signalSemaphores[vulkanCommandBuffer->signalSemaphoreCount] = swapchainData->renderFinishedSemaphore; - vulkanCommandBuffer->signalSemaphoreCount += 1; - - *pWidth = swapchainData->extent.width; - *pHeight = swapchainData->extent.height; - - return (Refresh_Texture*) swapchainTextureContainer; -} - -static Refresh_TextureFormat VULKAN_GetSwapchainFormat( - Refresh_Renderer *driverData, - void *windowHandle -) { - WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(windowHandle); - - if (windowData == NULL) - { - Refresh_LogWarn("Cannot get swapchain format, window has not been claimed!"); - return 0; - } - - if (windowData->swapchainData == NULL) - { - Refresh_LogWarn("Cannot get swapchain format, swapchain is currently invalid!"); - return 0; - } - - if (windowData->swapchainData->swapchainFormat == VK_FORMAT_R8G8B8A8_UNORM) - { - return REFRESH_TEXTUREFORMAT_R8G8B8A8; - } - else if (windowData->swapchainData->swapchainFormat == VK_FORMAT_B8G8R8A8_UNORM) - { - return REFRESH_TEXTUREFORMAT_B8G8R8A8; - } - else - { - Refresh_LogWarn("Unrecognized swapchain format!"); - return 0; - } -} - -static void VULKAN_SetSwapchainPresentMode( - Refresh_Renderer *driverData, - void *windowHandle, - Refresh_PresentMode presentMode -) { - WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(windowHandle); - - if (windowData == NULL) - { - Refresh_LogWarn("Cannot set present mode, window has not been claimed!"); - return; - } - - VULKAN_INTERNAL_RecreateSwapchain( - (VulkanRenderer *)driverData, - windowData - ); -} - -/* Submission structure */ - -static VkFence VULKAN_INTERNAL_AcquireFenceFromPool( - VulkanRenderer *renderer -) { - VkFenceCreateInfo fenceCreateInfo; - VkFence fence; - VkResult vulkanResult; - - if (renderer->fencePool.availableFenceCount == 0) - { - /* Create fence */ - fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceCreateInfo.pNext = NULL; - fenceCreateInfo.flags = 0; - - vulkanResult = renderer->vkCreateFence( - renderer->logicalDevice, - &fenceCreateInfo, - NULL, - &fence - ); - - if (vulkanResult != VK_SUCCESS) - { - LogVulkanResultAsError("vkCreateFence", vulkanResult); - return NULL; - } - - return fence; - } - - SDL_LockMutex(renderer->fencePool.lock); - - fence = renderer->fencePool.availableFences[renderer->fencePool.availableFenceCount - 1]; - renderer->fencePool.availableFenceCount -= 1; - - vulkanResult = renderer->vkResetFences( - renderer->logicalDevice, - 1, - &fence - ); - - if (vulkanResult != VK_SUCCESS) - { - LogVulkanResultAsError("vkResetFences", vulkanResult); - } - - SDL_UnlockMutex(renderer->fencePool.lock); - - return fence; -} - -static void VULKAN_INTERNAL_ReturnFenceToPool( - VulkanRenderer *renderer, - VkFence fence -) { - SDL_LockMutex(renderer->fencePool.lock); - - EXPAND_ARRAY_IF_NEEDED( - renderer->fencePool.availableFences, - VkFence, - renderer->fencePool.availableFenceCount + 1, - renderer->fencePool.availableFenceCapacity, - renderer->fencePool.availableFenceCapacity * 2 - ); - - renderer->fencePool.availableFences[renderer->fencePool.availableFenceCount] = fence; - renderer->fencePool.availableFenceCount += 1; - - SDL_UnlockMutex(renderer->fencePool.lock); -} - -static void VULKAN_INTERNAL_PerformPendingDestroys( - VulkanRenderer *renderer -) { - int32_t i; - - SDL_LockMutex(renderer->disposeLock); - - for (i = renderer->texturesToDestroyCount - 1; i >= 0; i -= 1) - { - if (SDL_AtomicGet(&renderer->texturesToDestroy[i]->referenceCount) == 0) - { - VULKAN_INTERNAL_DestroyTexture( - renderer, - renderer->texturesToDestroy[i] - ); - - renderer->texturesToDestroy[i] = renderer->texturesToDestroy[renderer->texturesToDestroyCount - 1]; - renderer->texturesToDestroyCount -= 1; - } - } - - for (i = renderer->buffersToDestroyCount - 1; i >= 0; i -= 1) - { - if (SDL_AtomicGet(&renderer->buffersToDestroy[i]->referenceCount) == 0) - { - VULKAN_INTERNAL_DestroyBuffer( - renderer, - renderer->buffersToDestroy[i]); - - renderer->buffersToDestroy[i] = renderer->buffersToDestroy[renderer->buffersToDestroyCount - 1]; - renderer->buffersToDestroyCount -= 1; - } - } - - for (i = renderer->graphicsPipelinesToDestroyCount - 1; i >= 0; i -= 1) - { - if (SDL_AtomicGet(&renderer->graphicsPipelinesToDestroy[i]->referenceCount) == 0) - { - VULKAN_INTERNAL_DestroyGraphicsPipeline( - renderer, - renderer->graphicsPipelinesToDestroy[i] - ); - - renderer->graphicsPipelinesToDestroy[i] = renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount - 1]; - renderer->graphicsPipelinesToDestroyCount -= 1; - } - } - - for (i = renderer->computePipelinesToDestroyCount - 1; i >= 0; i -= 1) - { - if (SDL_AtomicGet(&renderer->computePipelinesToDestroy[i]->referenceCount) == 0) - { - VULKAN_INTERNAL_DestroyComputePipeline( - renderer, - renderer->computePipelinesToDestroy[i] - ); - - renderer->computePipelinesToDestroy[i] = renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount - 1]; - renderer->computePipelinesToDestroyCount -= 1 ; - } - } - - for (i = renderer->shaderModulesToDestroyCount - 1; i >= 0; i -= 1) - { - if (SDL_AtomicGet(&renderer->shaderModulesToDestroy[i]->referenceCount) == 0) - { - VULKAN_INTERNAL_DestroyShaderModule( - renderer, - renderer->shaderModulesToDestroy[i] - ); - - renderer->shaderModulesToDestroy[i] = renderer->shaderModulesToDestroy[renderer->shaderModulesToDestroyCount - 1]; - renderer->shaderModulesToDestroyCount -= 1; - } - } - - for (i = renderer->samplersToDestroyCount - 1; i >= 0; i -= 1) - { - if (SDL_AtomicGet(&renderer->samplersToDestroy[i]->referenceCount) == 0) - { - VULKAN_INTERNAL_DestroySampler( - renderer, - renderer->samplersToDestroy[i] - ); - - renderer->samplersToDestroy[i] = renderer->samplersToDestroy[renderer->samplersToDestroyCount - 1]; - renderer->samplersToDestroyCount -= 1; - } - } - - for (i = renderer->framebuffersToDestroyCount - 1; i >= 0; i -= 1) - { - if (SDL_AtomicGet(&renderer->framebuffersToDestroy[i]->referenceCount) == 0) - { - VULKAN_INTERNAL_DestroyFramebuffer( - renderer, - renderer->framebuffersToDestroy[i] - ); - - renderer->framebuffersToDestroy[i] = renderer->framebuffersToDestroy[renderer->framebuffersToDestroyCount - 1]; - renderer->framebuffersToDestroyCount -= 1; - } - } - - SDL_UnlockMutex(renderer->disposeLock); -} - -static void VULKAN_INTERNAL_CleanCommandBuffer( - VulkanRenderer *renderer, - VulkanCommandBuffer *commandBuffer -) { - uint32_t i; - VulkanUniformBuffer *uniformBuffer; - DescriptorSetData *descriptorSetData; - - if (commandBuffer->autoReleaseFence) - { - VULKAN_INTERNAL_ReturnFenceToPool( - renderer, - commandBuffer->inFlightFence - ); - - commandBuffer->inFlightFence = VK_NULL_HANDLE; - } - - /* Bound uniform buffers are now available */ - - for (i = 0; i < commandBuffer->boundUniformBufferCount; i += 1) - { - uniformBuffer = commandBuffer->boundUniformBuffers[i]; - - SDL_LockMutex(uniformBuffer->pool->lock); - if (uniformBuffer->pool->availableBufferCount == uniformBuffer->pool->availableBufferCapacity) - { - uniformBuffer->pool->availableBufferCapacity *= 2; - uniformBuffer->pool->availableBuffers = SDL_realloc( - uniformBuffer->pool->availableBuffers, - uniformBuffer->pool->availableBufferCapacity * sizeof(VulkanUniformBuffer*) - ); - } - - uniformBuffer->pool->availableBuffers[uniformBuffer->pool->availableBufferCount] = uniformBuffer; - uniformBuffer->pool->availableBufferCount += 1; - SDL_UnlockMutex(uniformBuffer->pool->lock); - } - - commandBuffer->boundUniformBufferCount = 0; - - /* Clean up transfer buffers */ - - for (i = 0; i < commandBuffer->transferBufferCount; i += 1) - { - if (commandBuffer->transferBuffers[i]->fromPool) - { - SDL_LockMutex(renderer->transferBufferPool.lock); - - commandBuffer->transferBuffers[i]->offset = 0; - renderer->transferBufferPool.availableBuffers[renderer->transferBufferPool.availableBufferCount] = commandBuffer->transferBuffers[i]; - renderer->transferBufferPool.availableBufferCount += 1; - - SDL_UnlockMutex(renderer->transferBufferPool.lock); - } - else - { - VULKAN_INTERNAL_QueueDestroyBuffer(renderer, commandBuffer->transferBuffers[i]->buffer); - SDL_free(commandBuffer->transferBuffers[i]); - commandBuffer->transferBuffers[i] = NULL; - } - } - - commandBuffer->transferBufferCount = 0; - - /* Bound descriptor sets are now available */ - - for (i = 0; i < commandBuffer->boundDescriptorSetDataCount; i += 1) - { - descriptorSetData = &commandBuffer->boundDescriptorSetDatas[i]; - - SDL_LockMutex(descriptorSetData->descriptorSetCache->lock); - - if (descriptorSetData->descriptorSetCache->inactiveDescriptorSetCount == descriptorSetData->descriptorSetCache->inactiveDescriptorSetCapacity) - { - descriptorSetData->descriptorSetCache->inactiveDescriptorSetCapacity *= 2; - descriptorSetData->descriptorSetCache->inactiveDescriptorSets = SDL_realloc( - descriptorSetData->descriptorSetCache->inactiveDescriptorSets, - descriptorSetData->descriptorSetCache->inactiveDescriptorSetCapacity * sizeof(VkDescriptorSet) - ); - } - - descriptorSetData->descriptorSetCache->inactiveDescriptorSets[descriptorSetData->descriptorSetCache->inactiveDescriptorSetCount] = descriptorSetData->descriptorSet; - descriptorSetData->descriptorSetCache->inactiveDescriptorSetCount += 1; - - SDL_UnlockMutex(descriptorSetData->descriptorSetCache->lock); - } - - commandBuffer->boundDescriptorSetDataCount = 0; - - /* Decrement reference counts */ - - for (i = 0; i < commandBuffer->usedBufferCount; i += 1) - { - SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->referenceCount); - } - commandBuffer->usedBufferCount = 0; - - for (i = 0; i < commandBuffer->usedTextureCount; i += 1) - { - SDL_AtomicDecRef(&commandBuffer->usedTextures[i]->referenceCount); - } - commandBuffer->usedTextureCount = 0; - - for (i = 0; i < commandBuffer->usedSamplerCount; i += 1) - { - SDL_AtomicDecRef(&commandBuffer->usedSamplers[i]->referenceCount); - } - commandBuffer->usedSamplerCount = 0; - - for (i = 0; i < commandBuffer->usedGraphicsPipelineCount; i += 1) - { - SDL_AtomicDecRef(&commandBuffer->usedGraphicsPipelines[i]->referenceCount); - } - commandBuffer->usedGraphicsPipelineCount = 0; - - for (i = 0; i < commandBuffer->usedComputePipelineCount; i += 1) - { - SDL_AtomicDecRef(&commandBuffer->usedComputePipelines[i]->referenceCount); - } - commandBuffer->usedComputePipelineCount = 0; - - for (i = 0; i < commandBuffer->usedFramebufferCount; i += 1) - { - SDL_AtomicDecRef(&commandBuffer->usedFramebuffers[i]->referenceCount); - } - commandBuffer->usedFramebufferCount = 0; - - /* Reset presentation data */ - - commandBuffer->presentDataCount = 0; - commandBuffer->waitSemaphoreCount = 0; - commandBuffer->signalSemaphoreCount = 0; - - /* Return command buffer to pool */ - - SDL_LockMutex(renderer->acquireCommandBufferLock); - - if (commandBuffer->commandPool->inactiveCommandBufferCount == commandBuffer->commandPool->inactiveCommandBufferCapacity) - { - commandBuffer->commandPool->inactiveCommandBufferCapacity += 1; - commandBuffer->commandPool->inactiveCommandBuffers = SDL_realloc( - commandBuffer->commandPool->inactiveCommandBuffers, - commandBuffer->commandPool->inactiveCommandBufferCapacity * sizeof(VulkanCommandBuffer*) - ); - } - - commandBuffer->commandPool->inactiveCommandBuffers[ - commandBuffer->commandPool->inactiveCommandBufferCount - ] = commandBuffer; - commandBuffer->commandPool->inactiveCommandBufferCount += 1; - - SDL_UnlockMutex(renderer->acquireCommandBufferLock); - - /* Remove this command buffer from the submitted list */ - for (i = 0; i < renderer->submittedCommandBufferCount; i += 1) - { - if (renderer->submittedCommandBuffers[i] == commandBuffer) - { - renderer->submittedCommandBuffers[i] = renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount - 1]; - renderer->submittedCommandBufferCount -= 1; - } - } -} - -static void VULKAN_Wait( - Refresh_Renderer *driverData -) { - VulkanRenderer *renderer = (VulkanRenderer*) driverData; - VulkanCommandBuffer *commandBuffer; - VkResult result; - int32_t i; - - result = renderer->vkDeviceWaitIdle(renderer->logicalDevice); - - if (result != VK_SUCCESS) - { - LogVulkanResultAsError("vkDeviceWaitIdle", result); - return; - } - - SDL_LockMutex(renderer->submitLock); - - for (i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) - { - commandBuffer = renderer->submittedCommandBuffers[i]; - VULKAN_INTERNAL_CleanCommandBuffer(renderer, commandBuffer); - } - - VULKAN_INTERNAL_PerformPendingDestroys(renderer); - - SDL_UnlockMutex(renderer->submitLock); -} - -static Refresh_Fence* VULKAN_SubmitAndAcquireFence( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer -) { - VulkanCommandBuffer *vulkanCommandBuffer; - - VULKAN_Submit(driverData, commandBuffer); - - vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - vulkanCommandBuffer->autoReleaseFence = 0; - - return (Refresh_Fence*) vulkanCommandBuffer->inFlightFence; -} - -static void VULKAN_Submit( - Refresh_Renderer *driverData, - Refresh_CommandBuffer *commandBuffer -) { - VulkanRenderer* renderer = (VulkanRenderer*)driverData; - VkSubmitInfo submitInfo; - VkPresentInfoKHR presentInfo; - VulkanPresentData *presentData; - VkResult vulkanResult, presentResult = VK_SUCCESS; - VulkanCommandBuffer *vulkanCommandBuffer; - VkPipelineStageFlags waitStages[MAX_PRESENT_COUNT]; - uint32_t swapchainImageIndex; - uint8_t commandBufferCleaned = 0; - VulkanMemorySubAllocator *allocator; - int32_t i, j; - - SDL_LockMutex(renderer->submitLock); - - /* FIXME: Can this just be permanent? */ - for (i = 0; i < MAX_PRESENT_COUNT; i += 1) - { - waitStages[i] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - } - - vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; - - for (j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) - { - swapchainImageIndex = vulkanCommandBuffer->presentDatas[j].swapchainImageIndex; - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - vulkanCommandBuffer->commandBuffer, - RESOURCE_ACCESS_PRESENT, - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - 1, - 0, - 1, - 0, - vulkanCommandBuffer->presentDatas[j].windowData->swapchainData->textureContainers[swapchainImageIndex].vulkanTexture->image, - &vulkanCommandBuffer->presentDatas[j].windowData->swapchainData->textureContainers[swapchainImageIndex].vulkanTexture->resourceAccessType - ); - } - - VULKAN_INTERNAL_EndCommandBuffer(renderer, vulkanCommandBuffer); - - vulkanCommandBuffer->autoReleaseFence = 1; - vulkanCommandBuffer->inFlightFence = VULKAN_INTERNAL_AcquireFenceFromPool(renderer); - - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.pNext = NULL; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &vulkanCommandBuffer->commandBuffer; - - submitInfo.pWaitDstStageMask = waitStages; - submitInfo.pWaitSemaphores = vulkanCommandBuffer->waitSemaphores; - submitInfo.waitSemaphoreCount = vulkanCommandBuffer->waitSemaphoreCount; - submitInfo.pSignalSemaphores = vulkanCommandBuffer->signalSemaphores; - submitInfo.signalSemaphoreCount = vulkanCommandBuffer->signalSemaphoreCount; - - vulkanResult = renderer->vkQueueSubmit( - renderer->unifiedQueue, - 1, - &submitInfo, - vulkanCommandBuffer->inFlightFence - ); - - if (vulkanResult != VK_SUCCESS) - { - LogVulkanResultAsError("vkQueueSubmit", vulkanResult); - } - - /* Mark command buffers as submitted */ - - if (renderer->submittedCommandBufferCount + 1 >= renderer->submittedCommandBufferCapacity) - { - renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1; - - renderer->submittedCommandBuffers = SDL_realloc( - renderer->submittedCommandBuffers, - sizeof(VulkanCommandBuffer*) * renderer->submittedCommandBufferCapacity - ); - } - - renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = vulkanCommandBuffer; - renderer->submittedCommandBufferCount += 1; - - /* Present, if applicable */ - - for (j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) - { - presentData = &vulkanCommandBuffer->presentDatas[j]; - - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.pNext = NULL; - presentInfo.pWaitSemaphores = &presentData->windowData->swapchainData->renderFinishedSemaphore; - presentInfo.waitSemaphoreCount = 1; - presentInfo.pSwapchains = &presentData->windowData->swapchainData->swapchain; - presentInfo.swapchainCount = 1; - presentInfo.pImageIndices = &presentData->swapchainImageIndex; - presentInfo.pResults = NULL; - - presentResult = renderer->vkQueuePresentKHR( - renderer->unifiedQueue, - &presentInfo - ); - - if (presentResult != VK_SUCCESS) - { - VULKAN_INTERNAL_RecreateSwapchain( - renderer, - presentData->windowData - ); - } - } - - /* Check if we can perform any cleanups */ - - for (i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) - { - vulkanResult = renderer->vkGetFenceStatus( - renderer->logicalDevice, - renderer->submittedCommandBuffers[i]->inFlightFence - ); - - if (vulkanResult == VK_SUCCESS) - { - VULKAN_INTERNAL_CleanCommandBuffer( - renderer, - renderer->submittedCommandBuffers[i] - ); - - commandBufferCleaned = 1; - } - } - - if (commandBufferCleaned) - { - SDL_LockMutex(renderer->allocatorLock); - - for (i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) - { - allocator = &renderer->memoryAllocator->subAllocators[i]; - - for (j = allocator->allocationCount - 1; j >= 0; j -= 1) - { - if (allocator->allocations[j]->usedRegionCount == 0) - { - VULKAN_INTERNAL_DeallocateMemory( - renderer, - allocator, - j - ); - } - } - } - - SDL_UnlockMutex(renderer->allocatorLock); - } - - /* Check pending destroys */ - - VULKAN_INTERNAL_PerformPendingDestroys(renderer); - - /* Defrag! */ - if (renderer->needDefrag && !renderer->defragInProgress) - { - if (SDL_GetTicks64() >= renderer->defragTimestamp) - { - VULKAN_INTERNAL_DefragmentMemory(renderer); - } - } - - SDL_UnlockMutex(renderer->submitLock); -} - -static uint8_t VULKAN_INTERNAL_DefragmentMemory( - VulkanRenderer *renderer -) { - VulkanMemorySubAllocator allocator; - VulkanMemoryAllocation *allocation; - uint32_t allocationIndexToDefrag; - VulkanMemoryUsedRegion *currentRegion; - VulkanBuffer* newBuffer; - VulkanTexture* newTexture; - VkBufferCopy bufferCopy; - VkImageCopy *imageCopyRegions; - VulkanCommandBuffer *commandBuffer; - uint32_t i, level; - VulkanResourceAccessType copyResourceAccessType = RESOURCE_ACCESS_NONE; - VulkanResourceAccessType originalResourceAccessType; - - SDL_LockMutex(renderer->allocatorLock); - - renderer->needDefrag = 0; - renderer->defragInProgress = 1; - - commandBuffer = (VulkanCommandBuffer*) VULKAN_AcquireCommandBuffer((Refresh_Renderer *) renderer); - - if (VULKAN_INTERNAL_FindAllocationToDefragment( - renderer, - &allocator, - &allocationIndexToDefrag - )) { - allocation = allocator.allocations[allocationIndexToDefrag]; - - VULKAN_INTERNAL_MakeMemoryUnavailable( - renderer, - allocation - ); - - /* For each used region in the allocation - * create a new resource, copy the data - * and re-point the resource containers - */ - for (i = 0; i < allocation->usedRegionCount; i += 1) - { - currentRegion = allocation->usedRegions[i]; - copyResourceAccessType = RESOURCE_ACCESS_NONE; - - if (currentRegion->isBuffer) - { - currentRegion->vulkanBuffer->usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; - - newBuffer = VULKAN_INTERNAL_CreateBuffer( - renderer, - currentRegion->vulkanBuffer->size, - RESOURCE_ACCESS_NONE, - currentRegion->vulkanBuffer->usage, - currentRegion->vulkanBuffer->requireHostVisible, - currentRegion->vulkanBuffer->preferDeviceLocal, - 0 - ); - - if (newBuffer == NULL) - { - Refresh_LogError("Failed to create defrag buffer!"); - return 0; - } - - originalResourceAccessType = currentRegion->vulkanBuffer->resourceAccessType; - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - commandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_READ, - currentRegion->vulkanBuffer - ); - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - commandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - newBuffer - ); - - bufferCopy.srcOffset = 0; - bufferCopy.dstOffset = 0; - bufferCopy.size = currentRegion->resourceSize; - - renderer->vkCmdCopyBuffer( - commandBuffer->commandBuffer, - currentRegion->vulkanBuffer->buffer, - newBuffer->buffer, - 1, - &bufferCopy - ); - - VULKAN_INTERNAL_BufferMemoryBarrier( - renderer, - commandBuffer->commandBuffer, - originalResourceAccessType, - newBuffer - ); - - VULKAN_INTERNAL_TrackBuffer(renderer, commandBuffer, currentRegion->vulkanBuffer); - VULKAN_INTERNAL_TrackBuffer(renderer, commandBuffer, newBuffer); - - /* re-point original container to new buffer */ - if (currentRegion->vulkanBuffer->container != NULL) - { - newBuffer->container = currentRegion->vulkanBuffer->container; - newBuffer->container->vulkanBuffer = newBuffer; - currentRegion->vulkanBuffer->container = NULL; - } - - VULKAN_INTERNAL_QueueDestroyBuffer(renderer, currentRegion->vulkanBuffer); - - renderer->needDefrag = 1; - } - else - { - newTexture = VULKAN_INTERNAL_CreateTexture( - renderer, - currentRegion->vulkanTexture->dimensions.width, - currentRegion->vulkanTexture->dimensions.height, - currentRegion->vulkanTexture->depth, - currentRegion->vulkanTexture->isCube, - currentRegion->vulkanTexture->levelCount, - currentRegion->vulkanTexture->sampleCount, - currentRegion->vulkanTexture->format, - currentRegion->vulkanTexture->aspectFlags, - currentRegion->vulkanTexture->usageFlags - ); - - if (newTexture == NULL) - { - Refresh_LogError("Failed to create defrag texture!"); - return 0; - } - - originalResourceAccessType = currentRegion->vulkanTexture->resourceAccessType; - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - commandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_READ, - currentRegion->vulkanTexture->aspectFlags, - 0, - currentRegion->vulkanTexture->layerCount, - 0, - currentRegion->vulkanTexture->levelCount, - 0, - currentRegion->vulkanTexture->image, - ¤tRegion->vulkanTexture->resourceAccessType - ); - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - commandBuffer->commandBuffer, - RESOURCE_ACCESS_TRANSFER_WRITE, - currentRegion->vulkanTexture->aspectFlags, - 0, - currentRegion->vulkanTexture->layerCount, - 0, - currentRegion->vulkanTexture->levelCount, - 0, - newTexture->image, - ©ResourceAccessType - ); - - imageCopyRegions = SDL_stack_alloc(VkImageCopy, currentRegion->vulkanTexture->levelCount); - - for (level = 0; level < currentRegion->vulkanTexture->levelCount; level += 1) - { - imageCopyRegions[level].srcOffset.x = 0; - imageCopyRegions[level].srcOffset.y = 0; - imageCopyRegions[level].srcOffset.z = 0; - imageCopyRegions[level].srcSubresource.aspectMask = currentRegion->vulkanTexture->aspectFlags; - imageCopyRegions[level].srcSubresource.baseArrayLayer = 0; - imageCopyRegions[level].srcSubresource.layerCount = currentRegion->vulkanTexture->layerCount; - imageCopyRegions[level].srcSubresource.mipLevel = level; - imageCopyRegions[level].extent.width = SDL_max(1, currentRegion->vulkanTexture->dimensions.width >> level); - imageCopyRegions[level].extent.height = SDL_max(1, currentRegion->vulkanTexture->dimensions.height >> level); - imageCopyRegions[level].extent.depth = currentRegion->vulkanTexture->depth; - imageCopyRegions[level].dstOffset.x = 0; - imageCopyRegions[level].dstOffset.y = 0; - imageCopyRegions[level].dstOffset.z = 0; - imageCopyRegions[level].dstSubresource.aspectMask = currentRegion->vulkanTexture->aspectFlags; - imageCopyRegions[level].dstSubresource.baseArrayLayer = 0; - imageCopyRegions[level].dstSubresource.layerCount = currentRegion->vulkanTexture->layerCount; - imageCopyRegions[level].dstSubresource.mipLevel = level; - } - - renderer->vkCmdCopyImage( - commandBuffer->commandBuffer, - currentRegion->vulkanTexture->image, - AccessMap[currentRegion->vulkanTexture->resourceAccessType].imageLayout, - newTexture->image, - AccessMap[copyResourceAccessType].imageLayout, - currentRegion->vulkanTexture->levelCount, - imageCopyRegions - ); - - VULKAN_INTERNAL_ImageMemoryBarrier( - renderer, - commandBuffer->commandBuffer, - originalResourceAccessType, - currentRegion->vulkanTexture->aspectFlags, - 0, - currentRegion->vulkanTexture->layerCount, - 0, - currentRegion->vulkanTexture->levelCount, - 0, - newTexture->image, - ©ResourceAccessType - ); - - SDL_stack_free(imageCopyRegions); - - VULKAN_INTERNAL_TrackTexture(renderer, commandBuffer, currentRegion->vulkanTexture); - VULKAN_INTERNAL_TrackTexture(renderer, commandBuffer, newTexture); - - /* re-point original container to new texture */ - newTexture->container = currentRegion->vulkanTexture->container; - newTexture->container->vulkanTexture = newTexture; - currentRegion->vulkanTexture->container = NULL; - - VULKAN_INTERNAL_QueueDestroyTexture(renderer, currentRegion->vulkanTexture); - - renderer->needDefrag = 1; - } - } - } - - SDL_UnlockMutex(renderer->allocatorLock); - - renderer->defragTimestamp = SDL_GetTicks64() + DEFRAG_TIME; - - VULKAN_Submit( - (Refresh_Renderer*) renderer, - (Refresh_CommandBuffer*) commandBuffer - ); - - renderer->defragInProgress = 0; - - return 1; -} - -static void VULKAN_WaitForFences( - Refresh_Renderer *driverData, - uint8_t waitAll, - uint32_t fenceCount, - Refresh_Fence **pFences -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VkResult result; - - result = renderer->vkWaitForFences( - renderer->logicalDevice, - fenceCount, - (VkFence*) pFences, - waitAll, - UINT64_MAX - ); - - if (result != VK_SUCCESS) - { - LogVulkanResultAsError("vkWaitForFences", result); - } -} - -static int VULKAN_QueryFence( - Refresh_Renderer *driverData, - Refresh_Fence *fence -) { - VulkanRenderer* renderer = (VulkanRenderer*) driverData; - VkResult result; - - result = renderer->vkGetFenceStatus( - renderer->logicalDevice, - (VkFence) fence - ); - - if (result == VK_SUCCESS) - { - return 1; - } - else if (result == VK_NOT_READY) - { - return 0; - } - else - { - LogVulkanResultAsError("vkGetFenceStatus", result); - return -1; - } -} - -static void VULKAN_ReleaseFence( - Refresh_Renderer *driverData, - Refresh_Fence *fence -) { - VULKAN_INTERNAL_ReturnFenceToPool((VulkanRenderer*) driverData, (VkFence) fence); -} - -/* Device instantiation */ - -static inline uint8_t CheckDeviceExtensions( - VkExtensionProperties *extensions, - uint32_t numExtensions, - VulkanExtensions *supports -) { - uint32_t i; - - SDL_memset(supports, '\0', sizeof(VulkanExtensions)); - for (i = 0; i < numExtensions; i += 1) - { - const char *name = extensions[i].extensionName; - #define CHECK(ext) \ - if (SDL_strcmp(name, "VK_" #ext) == 0) \ - { \ - supports->ext = 1; \ - } - CHECK(KHR_swapchain) - else CHECK(KHR_maintenance1) - else CHECK(KHR_get_memory_requirements2) - else CHECK(KHR_driver_properties) - else CHECK(EXT_vertex_attribute_divisor) - else CHECK(KHR_portability_subset) - #undef CHECK - } - - return ( supports->KHR_swapchain && - supports->KHR_maintenance1 && - supports->KHR_get_memory_requirements2 ); -} - -static inline uint32_t GetDeviceExtensionCount(VulkanExtensions *supports) -{ - return ( - supports->KHR_swapchain + - supports->KHR_maintenance1 + - supports->KHR_get_memory_requirements2 + - supports->KHR_driver_properties + - supports->EXT_vertex_attribute_divisor + - supports->KHR_portability_subset - ); -} - -static inline void CreateDeviceExtensionArray( - VulkanExtensions *supports, - const char **extensions -) { - uint8_t cur = 0; - #define CHECK(ext) \ - if (supports->ext) \ - { \ - extensions[cur++] = "VK_" #ext; \ - } - CHECK(KHR_swapchain) - CHECK(KHR_maintenance1) - CHECK(KHR_get_memory_requirements2) - CHECK(KHR_driver_properties) - CHECK(EXT_vertex_attribute_divisor) - CHECK(KHR_portability_subset) - #undef CHECK -} - -static inline uint8_t SupportsInstanceExtension( - const char *ext, - VkExtensionProperties *availableExtensions, - uint32_t numAvailableExtensions -) { - uint32_t i; - for (i = 0; i < numAvailableExtensions; i += 1) - { - if (SDL_strcmp(ext, availableExtensions[i].extensionName) == 0) - { - return 1; - } - } - return 0; -} - -static uint8_t VULKAN_INTERNAL_CheckInstanceExtensions( - const char **requiredExtensions, - uint32_t requiredExtensionsLength, - uint8_t *supportsDebugUtils -) { - uint32_t extensionCount, i; - VkExtensionProperties *availableExtensions; - uint8_t allExtensionsSupported = 1; - - vkEnumerateInstanceExtensionProperties( - NULL, - &extensionCount, - NULL - ); - availableExtensions = SDL_malloc( - extensionCount * sizeof(VkExtensionProperties) - ); - vkEnumerateInstanceExtensionProperties( - NULL, - &extensionCount, - availableExtensions - ); - - for (i = 0; i < requiredExtensionsLength; i += 1) - { - if (!SupportsInstanceExtension( - requiredExtensions[i], - availableExtensions, - extensionCount - )) { - allExtensionsSupported = 0; - break; - } - } - - /* This is optional, but nice to have! */ - *supportsDebugUtils = SupportsInstanceExtension( - VK_EXT_DEBUG_UTILS_EXTENSION_NAME, - availableExtensions, - extensionCount - ); - - SDL_free(availableExtensions); - return allExtensionsSupported; -} - -static uint8_t VULKAN_INTERNAL_CheckDeviceExtensions( - VulkanRenderer *renderer, - VkPhysicalDevice physicalDevice, - VulkanExtensions *physicalDeviceExtensions -) { - uint32_t extensionCount; - VkExtensionProperties *availableExtensions; - uint8_t allExtensionsSupported; - - renderer->vkEnumerateDeviceExtensionProperties( - physicalDevice, - NULL, - &extensionCount, - NULL - ); - availableExtensions = (VkExtensionProperties*) SDL_malloc( - extensionCount * sizeof(VkExtensionProperties) - ); - renderer->vkEnumerateDeviceExtensionProperties( - physicalDevice, - NULL, - &extensionCount, - availableExtensions - ); - - allExtensionsSupported = CheckDeviceExtensions( - availableExtensions, - extensionCount, - physicalDeviceExtensions - ); - - SDL_free(availableExtensions); - return allExtensionsSupported; -} - -static uint8_t VULKAN_INTERNAL_CheckValidationLayers( - const char** validationLayers, - uint32_t validationLayersLength -) { - uint32_t layerCount; - VkLayerProperties *availableLayers; - uint32_t i, j; - uint8_t layerFound; - - vkEnumerateInstanceLayerProperties(&layerCount, NULL); - availableLayers = (VkLayerProperties*) SDL_malloc( - layerCount * sizeof(VkLayerProperties) - ); - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers); - - for (i = 0; i < validationLayersLength; i += 1) - { - layerFound = 0; - - for (j = 0; j < layerCount; j += 1) - { - if (SDL_strcmp(validationLayers[i], availableLayers[j].layerName) == 0) - { - layerFound = 1; - break; - } - } - - if (!layerFound) - { - break; - } - } - - SDL_free(availableLayers); - return layerFound; -} - -static uint8_t VULKAN_INTERNAL_CreateInstance( - VulkanRenderer *renderer, - void *deviceWindowHandle -) { - VkResult vulkanResult; - VkApplicationInfo appInfo; - const char **instanceExtensionNames; - uint32_t instanceExtensionCount; - VkInstanceCreateInfo createInfo; - static const char *layerNames[] = { "VK_LAYER_KHRONOS_validation" }; - - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pNext = NULL; - appInfo.pApplicationName = NULL; - appInfo.applicationVersion = 0; - appInfo.pEngineName = "REFRESH"; - appInfo.engineVersion = REFRESH_COMPILED_VERSION; - appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0); - - if (!SDL_Vulkan_GetInstanceExtensions( - (SDL_Window*) deviceWindowHandle, - &instanceExtensionCount, - NULL - )) { - Refresh_LogError( - "SDL_Vulkan_GetInstanceExtensions(): getExtensionCount: %s", - SDL_GetError() - ); - - return 0; - } - - /* Extra space for the following extensions: - * VK_KHR_get_physical_device_properties2 - * VK_EXT_debug_utils - */ - instanceExtensionNames = SDL_stack_alloc( - const char*, - instanceExtensionCount + 2 - ); - - if (!SDL_Vulkan_GetInstanceExtensions( - (SDL_Window*) deviceWindowHandle, - &instanceExtensionCount, - instanceExtensionNames - )) { - Refresh_LogError( - "SDL_Vulkan_GetInstanceExtensions(): %s", - SDL_GetError() - ); - - SDL_stack_free((char*) instanceExtensionNames); - return 0; - } - - /* Core since 1.1 */ - instanceExtensionNames[instanceExtensionCount++] = - VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; - - if (!VULKAN_INTERNAL_CheckInstanceExtensions( - instanceExtensionNames, - instanceExtensionCount, - &renderer->supportsDebugUtils - )) { - Refresh_LogError( - "Required Vulkan instance extensions not supported" - ); - - SDL_stack_free((char*) instanceExtensionNames); - return 0; - } - - if (renderer->supportsDebugUtils) - { - /* Append the debug extension to the end */ - instanceExtensionNames[instanceExtensionCount++] = - VK_EXT_DEBUG_UTILS_EXTENSION_NAME; - } - else - { - Refresh_LogWarn( - "%s is not supported!", - VK_EXT_DEBUG_UTILS_EXTENSION_NAME - ); - } - - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - createInfo.pNext = NULL; - createInfo.flags = 0; - createInfo.pApplicationInfo = &appInfo; - createInfo.ppEnabledLayerNames = layerNames; - createInfo.enabledExtensionCount = instanceExtensionCount; - createInfo.ppEnabledExtensionNames = instanceExtensionNames; - if (renderer->debugMode) - { - createInfo.enabledLayerCount = SDL_arraysize(layerNames); - if (!VULKAN_INTERNAL_CheckValidationLayers( - layerNames, - createInfo.enabledLayerCount - )) { - Refresh_LogWarn("Validation layers not found, continuing without validation"); - createInfo.enabledLayerCount = 0; - } - else - { - Refresh_LogInfo("Validation layers enabled, expect debug level performance!"); - } - } - else - { - createInfo.enabledLayerCount = 0; - } - - vulkanResult = vkCreateInstance(&createInfo, NULL, &renderer->instance); - if (vulkanResult != VK_SUCCESS) - { - Refresh_LogError( - "vkCreateInstance failed: %s", - VkErrorMessages(vulkanResult) - ); - - SDL_stack_free((char*) instanceExtensionNames); - return 0; - } - - SDL_stack_free((char*) instanceExtensionNames); - return 1; -} - -static uint8_t VULKAN_INTERNAL_IsDeviceSuitable( - VulkanRenderer *renderer, - VkPhysicalDevice physicalDevice, - VulkanExtensions *physicalDeviceExtensions, - VkSurfaceKHR surface, - uint32_t *queueFamilyIndex, - uint8_t *deviceRank -) { - uint32_t queueFamilyCount, queueFamilyRank, queueFamilyBest; - SwapChainSupportDetails swapchainSupportDetails; - VkQueueFamilyProperties *queueProps; - VkBool32 supportsPresent; - uint8_t querySuccess; - VkPhysicalDeviceProperties deviceProperties; - uint32_t i; - - /* Get the device rank before doing any checks, in case one fails. - * Note: If no dedicated device exists, one that supports our features - * would be fine - */ - renderer->vkGetPhysicalDeviceProperties( - physicalDevice, - &deviceProperties - ); - if (*deviceRank < DEVICE_PRIORITY[deviceProperties.deviceType]) - { - /* This device outranks the best device we've found so far! - * This includes a dedicated GPU that has less features than an - * integrated GPU, because this is a freak case that is almost - * never intentionally desired by the end user - */ - *deviceRank = DEVICE_PRIORITY[deviceProperties.deviceType]; - } - else if (*deviceRank > DEVICE_PRIORITY[deviceProperties.deviceType]) - { - /* Device is outranked by a previous device, don't even try to - * run a query and reset the rank to avoid overwrites - */ - *deviceRank = 0; - return 0; - } - - if (!VULKAN_INTERNAL_CheckDeviceExtensions( - renderer, - physicalDevice, - physicalDeviceExtensions - )) { - return 0; - } - - renderer->vkGetPhysicalDeviceQueueFamilyProperties( - physicalDevice, - &queueFamilyCount, - NULL - ); - - queueProps = (VkQueueFamilyProperties*) SDL_stack_alloc( - VkQueueFamilyProperties, - queueFamilyCount - ); - renderer->vkGetPhysicalDeviceQueueFamilyProperties( - physicalDevice, - &queueFamilyCount, - queueProps - ); - - queueFamilyBest = 0; - *queueFamilyIndex = UINT32_MAX; - for (i = 0; i < queueFamilyCount; i += 1) - { - renderer->vkGetPhysicalDeviceSurfaceSupportKHR( - physicalDevice, - i, - surface, - &supportsPresent - ); - if ( !supportsPresent || - !(queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) ) - { - /* Not a graphics family, ignore. */ - continue; - } - - /* The queue family bitflags are kind of annoying. - * - * We of course need a graphics family, but we ideally want the - * _primary_ graphics family. The spec states that at least one - * graphics family must also be a compute family, so generally - * drivers make that the first one. But hey, maybe something - * genuinely can't do compute or something, and FNA doesn't - * need it, so we'll be open to a non-compute queue family. - * - * Additionally, it's common to see the primary queue family - * have the transfer bit set, which is great! But this is - * actually optional; it's impossible to NOT have transfers in - * graphics/compute but it _is_ possible for a graphics/compute - * family, even the primary one, to just decide not to set the - * bitflag. Admittedly, a driver may want to isolate transfer - * queues to a dedicated family so that queues made solely for - * transfers can have an optimized DMA queue. - * - * That, or the driver author got lazy and decided not to set - * the bit. Looking at you, Android. - * - * -flibit - */ - if (queueProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT) - { - if (queueProps[i].queueFlags & VK_QUEUE_TRANSFER_BIT) - { - /* Has all attribs! */ - queueFamilyRank = 3; - } - else - { - /* Probably has a DMA transfer queue family */ - queueFamilyRank = 2; - } - } - else - { - /* Just a graphics family, probably has something better */ - queueFamilyRank = 1; - } - if (queueFamilyRank > queueFamilyBest) - { - *queueFamilyIndex = i; - queueFamilyBest = queueFamilyRank; - } - } - - SDL_stack_free(queueProps); - - if (*queueFamilyIndex == UINT32_MAX) - { - /* Somehow no graphics queues existed. Compute-only device? */ - return 0; - } - - /* FIXME: Need better structure for checking vs storing support details */ - querySuccess = VULKAN_INTERNAL_QuerySwapChainSupport( - renderer, - physicalDevice, - surface, - &swapchainSupportDetails - ); - if (swapchainSupportDetails.formatsLength > 0) - { - SDL_free(swapchainSupportDetails.formats); - } - if (swapchainSupportDetails.presentModesLength > 0) - { - SDL_free(swapchainSupportDetails.presentModes); - } - - return ( querySuccess && - swapchainSupportDetails.formatsLength > 0 && - swapchainSupportDetails.presentModesLength > 0 ); -} - -static uint8_t VULKAN_INTERNAL_DeterminePhysicalDevice( - VulkanRenderer *renderer, - VkSurfaceKHR surface -) { - VkResult vulkanResult; - VkPhysicalDevice *physicalDevices; - VulkanExtensions *physicalDeviceExtensions; - uint32_t physicalDeviceCount, i, suitableIndex; - uint32_t queueFamilyIndex, suitableQueueFamilyIndex; - uint8_t deviceRank, highestRank; - - vulkanResult = renderer->vkEnumeratePhysicalDevices( - renderer->instance, - &physicalDeviceCount, - NULL - ); - VULKAN_ERROR_CHECK(vulkanResult, vkEnumeratePhysicalDevices, 0) - - if (physicalDeviceCount == 0) - { - Refresh_LogWarn("Failed to find any GPUs with Vulkan support"); - return 0; - } - - physicalDevices = SDL_stack_alloc(VkPhysicalDevice, physicalDeviceCount); - physicalDeviceExtensions = SDL_stack_alloc(VulkanExtensions, physicalDeviceCount); - - vulkanResult = renderer->vkEnumeratePhysicalDevices( - renderer->instance, - &physicalDeviceCount, - physicalDevices - ); - - /* This should be impossible to hit, but from what I can tell this can - * be triggered not because the array is too small, but because there - * were drivers that turned out to be bogus, so this is the loader's way - * of telling us that the list is now smaller than expected :shrug: - */ - if (vulkanResult == VK_INCOMPLETE) - { - Refresh_LogWarn("vkEnumeratePhysicalDevices returned VK_INCOMPLETE, will keep trying anyway..."); - vulkanResult = VK_SUCCESS; - } - - if (vulkanResult != VK_SUCCESS) - { - Refresh_LogWarn( - "vkEnumeratePhysicalDevices failed: %s", - VkErrorMessages(vulkanResult) - ); - SDL_stack_free(physicalDevices); - SDL_stack_free(physicalDeviceExtensions); - return 0; - } - - /* Any suitable device will do, but we'd like the best */ - suitableIndex = -1; - highestRank = 0; - for (i = 0; i < physicalDeviceCount; i += 1) - { - deviceRank = highestRank; - if (VULKAN_INTERNAL_IsDeviceSuitable( - renderer, - physicalDevices[i], - &physicalDeviceExtensions[i], - surface, - &queueFamilyIndex, - &deviceRank - )) { - /* Use this for rendering. - * Note that this may override a previous device that - * supports rendering, but shares the same device rank. - */ - suitableIndex = i; - suitableQueueFamilyIndex = queueFamilyIndex; - highestRank = deviceRank; - } - else if (deviceRank > highestRank) - { - /* In this case, we found a... "realer?" GPU, - * but it doesn't actually support our Vulkan. - * We should disqualify all devices below as a - * result, because if we don't we end up - * ignoring real hardware and risk using - * something like LLVMpipe instead! - * -flibit - */ - suitableIndex = -1; - highestRank = deviceRank; - } - } - - if (suitableIndex != -1) - { - renderer->supports = physicalDeviceExtensions[suitableIndex]; - renderer->physicalDevice = physicalDevices[suitableIndex]; - renderer->queueFamilyIndex = suitableQueueFamilyIndex; - } - else - { - SDL_stack_free(physicalDevices); - SDL_stack_free(physicalDeviceExtensions); - return 0; - } - - renderer->physicalDeviceProperties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - if (renderer->supports.KHR_driver_properties) - { - renderer->physicalDeviceDriverProperties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR; - renderer->physicalDeviceDriverProperties.pNext = NULL; - - renderer->physicalDeviceProperties.pNext = - &renderer->physicalDeviceDriverProperties; - } - else - { - renderer->physicalDeviceProperties.pNext = NULL; - } - - renderer->vkGetPhysicalDeviceProperties2KHR( - renderer->physicalDevice, - &renderer->physicalDeviceProperties - ); - - renderer->vkGetPhysicalDeviceMemoryProperties( - renderer->physicalDevice, - &renderer->memoryProperties - ); - - SDL_stack_free(physicalDevices); - SDL_stack_free(physicalDeviceExtensions); - return 1; -} - -static uint8_t VULKAN_INTERNAL_CreateLogicalDevice( - VulkanRenderer *renderer -) { - VkResult vulkanResult; - VkDeviceCreateInfo deviceCreateInfo; - VkPhysicalDeviceFeatures deviceFeatures; - VkPhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures; - const char **deviceExtensions; - - VkDeviceQueueCreateInfo queueCreateInfo; - float queuePriority = 1.0f; - - queueCreateInfo.sType = - VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.pNext = NULL; - queueCreateInfo.flags = 0; - queueCreateInfo.queueFamilyIndex = renderer->queueFamilyIndex; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = &queuePriority; - - /* specifying used device features */ - - SDL_zero(deviceFeatures); - deviceFeatures.fillModeNonSolid = VK_TRUE; - deviceFeatures.samplerAnisotropy = VK_TRUE; - deviceFeatures.multiDrawIndirect = VK_TRUE; - deviceFeatures.independentBlend = VK_TRUE; - - /* creating the logical device */ - - deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - if (renderer->supports.KHR_portability_subset) - { - portabilityFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR; - portabilityFeatures.pNext = NULL; - portabilityFeatures.constantAlphaColorBlendFactors = VK_FALSE; - portabilityFeatures.events = VK_FALSE; - portabilityFeatures.imageViewFormatReinterpretation = VK_FALSE; - portabilityFeatures.imageViewFormatSwizzle = VK_TRUE; - portabilityFeatures.imageView2DOn3DImage = VK_FALSE; - portabilityFeatures.multisampleArrayImage = VK_FALSE; - portabilityFeatures.mutableComparisonSamplers = VK_FALSE; - portabilityFeatures.pointPolygons = VK_FALSE; - portabilityFeatures.samplerMipLodBias = VK_FALSE; /* Technically should be true, but eh */ - portabilityFeatures.separateStencilMaskRef = VK_FALSE; - portabilityFeatures.shaderSampleRateInterpolationFunctions = VK_FALSE; - portabilityFeatures.tessellationIsolines = VK_FALSE; - portabilityFeatures.tessellationPointMode = VK_FALSE; - portabilityFeatures.triangleFans = VK_FALSE; - portabilityFeatures.vertexAttributeAccessBeyondStride = VK_FALSE; - deviceCreateInfo.pNext = &portabilityFeatures; - } - else - { - deviceCreateInfo.pNext = NULL; - } - deviceCreateInfo.flags = 0; - deviceCreateInfo.queueCreateInfoCount = 1; - deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; - deviceCreateInfo.enabledLayerCount = 0; - deviceCreateInfo.ppEnabledLayerNames = NULL; - deviceCreateInfo.enabledExtensionCount = GetDeviceExtensionCount( - &renderer->supports - ); - deviceExtensions = SDL_stack_alloc( - const char*, - deviceCreateInfo.enabledExtensionCount - ); - CreateDeviceExtensionArray(&renderer->supports, deviceExtensions); - deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions; - deviceCreateInfo.pEnabledFeatures = &deviceFeatures; - - vulkanResult = renderer->vkCreateDevice( - renderer->physicalDevice, - &deviceCreateInfo, - NULL, - &renderer->logicalDevice - ); - SDL_stack_free(deviceExtensions); - VULKAN_ERROR_CHECK(vulkanResult, vkCreateDevice, 0) - - /* Load vkDevice entry points */ - - #define VULKAN_DEVICE_FUNCTION(ext, ret, func, params) \ - renderer->func = (vkfntype_##func) \ - renderer->vkGetDeviceProcAddr( \ - renderer->logicalDevice, \ - #func \ - ); - #include "Refresh_Driver_Vulkan_vkfuncs.h" - - renderer->vkGetDeviceQueue( - renderer->logicalDevice, - renderer->queueFamilyIndex, - 0, - &renderer->unifiedQueue - ); - - return 1; -} - -static void VULKAN_INTERNAL_LoadEntryPoints(void) -{ - /* Required for MoltenVK support */ - SDL_setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1); - - /* Load Vulkan entry points */ - if (SDL_Vulkan_LoadLibrary(NULL) < 0) - { - Refresh_LogWarn("Vulkan: SDL_Vulkan_LoadLibrary failed!"); - return; - } - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" - vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr(); - #pragma GCC diagnostic pop - if (vkGetInstanceProcAddr == NULL) - { - Refresh_LogWarn( - "SDL_Vulkan_GetVkGetInstanceProcAddr(): %s", - SDL_GetError() - ); - return; - } - - #define VULKAN_GLOBAL_FUNCTION(name) \ - name = (PFN_##name) vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \ - if (name == NULL) \ - { \ - Refresh_LogWarn("vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \ - return; \ - } - #include "Refresh_Driver_Vulkan_vkfuncs.h" -} - -static uint8_t VULKAN_INTERNAL_PrepareVulkan( - VulkanRenderer *renderer -) { - SDL_Window *dummyWindowHandle; - VkSurfaceKHR surface; - - VULKAN_INTERNAL_LoadEntryPoints(); - - dummyWindowHandle = SDL_CreateWindow( - "Refresh Vulkan", - 0, 0, - 128, 128, - SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN - ); - - if (dummyWindowHandle == NULL) - { - Refresh_LogWarn("Vulkan: Could not create dummy window"); - return 0; - } - - if (!VULKAN_INTERNAL_CreateInstance(renderer, dummyWindowHandle)) - { - SDL_DestroyWindow(dummyWindowHandle); - SDL_free(renderer); - Refresh_LogWarn("Vulkan: Could not create Vulkan instance"); - return 0; - } - - if (!SDL_Vulkan_CreateSurface( - (SDL_Window*) dummyWindowHandle, - renderer->instance, - &surface - )) { - SDL_DestroyWindow(dummyWindowHandle); - SDL_free(renderer); - Refresh_LogWarn( - "SDL_Vulkan_CreateSurface failed: %s", - SDL_GetError() - ); - return 0; - } - - #define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \ - renderer->func = (vkfntype_##func) vkGetInstanceProcAddr(renderer->instance, #func); - #include "Refresh_Driver_Vulkan_vkfuncs.h" - - if (!VULKAN_INTERNAL_DeterminePhysicalDevice(renderer, surface)) - { - return 0; - } - - renderer->vkDestroySurfaceKHR( - renderer->instance, - surface, - NULL - ); - SDL_DestroyWindow(dummyWindowHandle); - - return 1; -} - -static uint8_t VULKAN_PrepareDriver(uint32_t *flags) -{ - /* Set up dummy VulkanRenderer */ - VulkanRenderer *renderer = (VulkanRenderer*) SDL_malloc(sizeof(VulkanRenderer)); - uint8_t result; - - SDL_memset(renderer, '\0', sizeof(VulkanRenderer)); - - result = VULKAN_INTERNAL_PrepareVulkan(renderer); - - if (!result) - { - Refresh_LogWarn("Vulkan: Failed to determine a suitable physical device"); - } - else - { - *flags = SDL_WINDOW_VULKAN; - } - - renderer->vkDestroyInstance(renderer->instance, NULL); - SDL_free(renderer); - return result; -} - -static Refresh_Device* VULKAN_CreateDevice( - uint8_t debugMode -) { - VulkanRenderer *renderer = (VulkanRenderer*) SDL_malloc(sizeof(VulkanRenderer)); - - Refresh_Device *result; - VkResult vulkanResult; - uint32_t i; - - /* Variables: Descriptor set layouts */ - VkDescriptorSetLayoutCreateInfo setLayoutCreateInfo; - VkDescriptorSetLayoutBinding vertexParamLayoutBinding; - VkDescriptorSetLayoutBinding fragmentParamLayoutBinding; - VkDescriptorSetLayoutBinding computeParamLayoutBinding; - - VkDescriptorSetLayoutBinding emptyVertexSamplerLayoutBinding; - VkDescriptorSetLayoutBinding emptyFragmentSamplerLayoutBinding; - VkDescriptorSetLayoutBinding emptyComputeBufferDescriptorSetLayoutBinding; - VkDescriptorSetLayoutBinding emptyComputeImageDescriptorSetLayoutBinding; - - /* Variables: UBO Creation */ - VkDescriptorPoolCreateInfo defaultDescriptorPoolInfo; - VkDescriptorPoolSize poolSizes[4]; - VkDescriptorSetAllocateInfo descriptorAllocateInfo; - - /* Variables: Image Format Detection */ - VkImageFormatProperties imageFormatProperties; - - /* Variables: Transfer buffer init */ - VulkanTransferBuffer *transferBuffer; - - SDL_memset(renderer, '\0', sizeof(VulkanRenderer)); - renderer->debugMode = debugMode; - - if (!VULKAN_INTERNAL_PrepareVulkan(renderer)) - { - Refresh_LogError("Failed to initialize Vulkan!"); - return NULL; - } - - Refresh_LogInfo("Refresh Driver: Vulkan"); - Refresh_LogInfo( - "Vulkan Device: %s", - renderer->physicalDeviceProperties.properties.deviceName - ); - Refresh_LogInfo( - "Vulkan Driver: %s %s", - renderer->physicalDeviceDriverProperties.driverName, - renderer->physicalDeviceDriverProperties.driverInfo - ); - Refresh_LogInfo( - "Vulkan Conformance: %u.%u.%u", - renderer->physicalDeviceDriverProperties.conformanceVersion.major, - renderer->physicalDeviceDriverProperties.conformanceVersion.minor, - renderer->physicalDeviceDriverProperties.conformanceVersion.patch - ); - - if (!VULKAN_INTERNAL_CreateLogicalDevice( - renderer - )) { - Refresh_LogError("Failed to create logical device"); - return NULL; - } - - /* FIXME: just move this into this function */ - result = (Refresh_Device*) SDL_malloc(sizeof(Refresh_Device)); - ASSIGN_DRIVER(VULKAN) - - result->driverData = (Refresh_Renderer*) renderer; - - /* - * Create initial swapchain array - */ - - renderer->claimedWindowCapacity = 1; - renderer->claimedWindowCount = 0; - renderer->claimedWindows = SDL_malloc( - renderer->claimedWindowCapacity * sizeof(WindowData*) - ); - - /* Threading */ - - renderer->allocatorLock = SDL_CreateMutex(); - renderer->disposeLock = SDL_CreateMutex(); - renderer->submitLock = SDL_CreateMutex(); - renderer->acquireCommandBufferLock = SDL_CreateMutex(); - renderer->renderPassFetchLock = SDL_CreateMutex(); - renderer->framebufferFetchLock = SDL_CreateMutex(); - renderer->renderTargetFetchLock = SDL_CreateMutex(); - - /* - * Create submitted command buffer list - */ - - renderer->submittedCommandBufferCapacity = 16; - renderer->submittedCommandBufferCount = 0; - renderer->submittedCommandBuffers = SDL_malloc(sizeof(VulkanCommandBuffer*) * renderer->submittedCommandBufferCapacity); - - /* Memory Allocator */ - - renderer->memoryAllocator = (VulkanMemoryAllocator*) SDL_malloc( - sizeof(VulkanMemoryAllocator) - ); - - for (i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) - { - renderer->memoryAllocator->subAllocators[i].memoryTypeIndex = i; - renderer->memoryAllocator->subAllocators[i].nextAllocationSize = STARTING_ALLOCATION_SIZE; - renderer->memoryAllocator->subAllocators[i].allocations = NULL; - renderer->memoryAllocator->subAllocators[i].allocationCount = 0; - renderer->memoryAllocator->subAllocators[i].sortedFreeRegions = SDL_malloc( - sizeof(VulkanMemoryFreeRegion*) * 4 - ); - renderer->memoryAllocator->subAllocators[i].sortedFreeRegionCount = 0; - renderer->memoryAllocator->subAllocators[i].sortedFreeRegionCapacity = 4; - } - - /* Set up UBO layouts */ - - renderer->minUBOAlignment = renderer->physicalDeviceProperties.properties.limits.minUniformBufferOffsetAlignment; - - emptyVertexSamplerLayoutBinding.binding = 0; - emptyVertexSamplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - emptyVertexSamplerLayoutBinding.descriptorCount = 0; - emptyVertexSamplerLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - emptyVertexSamplerLayoutBinding.pImmutableSamplers = NULL; - - setLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - setLayoutCreateInfo.pNext = NULL; - setLayoutCreateInfo.flags = 0; - setLayoutCreateInfo.bindingCount = 1; - setLayoutCreateInfo.pBindings = &emptyVertexSamplerLayoutBinding; - - vulkanResult = renderer->vkCreateDescriptorSetLayout( - renderer->logicalDevice, - &setLayoutCreateInfo, - NULL, - &renderer->emptyVertexSamplerLayout - ); - - emptyFragmentSamplerLayoutBinding.binding = 0; - emptyFragmentSamplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - emptyFragmentSamplerLayoutBinding.descriptorCount = 0; - emptyFragmentSamplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - emptyFragmentSamplerLayoutBinding.pImmutableSamplers = NULL; - - setLayoutCreateInfo.pBindings = &emptyFragmentSamplerLayoutBinding; - - vulkanResult = renderer->vkCreateDescriptorSetLayout( - renderer->logicalDevice, - &setLayoutCreateInfo, - NULL, - &renderer->emptyFragmentSamplerLayout - ); - - emptyComputeBufferDescriptorSetLayoutBinding.binding = 0; - emptyComputeBufferDescriptorSetLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - emptyComputeBufferDescriptorSetLayoutBinding.descriptorCount = 0; - emptyComputeBufferDescriptorSetLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - emptyComputeBufferDescriptorSetLayoutBinding.pImmutableSamplers = NULL; - - setLayoutCreateInfo.pBindings = &emptyComputeBufferDescriptorSetLayoutBinding; - - vulkanResult = renderer->vkCreateDescriptorSetLayout( - renderer->logicalDevice, - &setLayoutCreateInfo, - NULL, - &renderer->emptyComputeBufferDescriptorSetLayout - ); - - emptyComputeImageDescriptorSetLayoutBinding.binding = 0; - emptyComputeImageDescriptorSetLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - emptyComputeImageDescriptorSetLayoutBinding.descriptorCount = 0; - emptyComputeImageDescriptorSetLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - emptyComputeImageDescriptorSetLayoutBinding.pImmutableSamplers = NULL; - - setLayoutCreateInfo.pBindings = &emptyComputeImageDescriptorSetLayoutBinding; - - vulkanResult = renderer->vkCreateDescriptorSetLayout( - renderer->logicalDevice, - &setLayoutCreateInfo, - NULL, - &renderer->emptyComputeImageDescriptorSetLayout - ); - - vertexParamLayoutBinding.binding = 0; - vertexParamLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - vertexParamLayoutBinding.descriptorCount = 1; - vertexParamLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - vertexParamLayoutBinding.pImmutableSamplers = NULL; - - setLayoutCreateInfo.bindingCount = 1; - setLayoutCreateInfo.pBindings = &vertexParamLayoutBinding; - - vulkanResult = renderer->vkCreateDescriptorSetLayout( - renderer->logicalDevice, - &setLayoutCreateInfo, - NULL, - &renderer->vertexUniformDescriptorSetLayout - ); - - if (vulkanResult != VK_SUCCESS) - { - Refresh_LogError("Failed to create vertex UBO layout!"); - return NULL; - } - - fragmentParamLayoutBinding.binding = 0; - fragmentParamLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - fragmentParamLayoutBinding.descriptorCount = 1; - fragmentParamLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - fragmentParamLayoutBinding.pImmutableSamplers = NULL; - - setLayoutCreateInfo.bindingCount = 1; - setLayoutCreateInfo.pBindings = &fragmentParamLayoutBinding; - - vulkanResult = renderer->vkCreateDescriptorSetLayout( - renderer->logicalDevice, - &setLayoutCreateInfo, - NULL, - &renderer->fragmentUniformDescriptorSetLayout - ); - - if (vulkanResult != VK_SUCCESS) - { - Refresh_LogError("Failed to create fragment UBO layout!"); - return NULL; - } - - computeParamLayoutBinding.binding = 0; - computeParamLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - computeParamLayoutBinding.descriptorCount = 1; - computeParamLayoutBinding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - computeParamLayoutBinding.pImmutableSamplers = NULL; - - setLayoutCreateInfo.bindingCount = 1; - setLayoutCreateInfo.pBindings = &computeParamLayoutBinding; - - vulkanResult = renderer->vkCreateDescriptorSetLayout( - renderer->logicalDevice, - &setLayoutCreateInfo, - NULL, - &renderer->computeUniformDescriptorSetLayout - ); - - /* Default Descriptors */ - - poolSizes[0].descriptorCount = 2; - poolSizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - - poolSizes[1].descriptorCount = 1; - poolSizes[1].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - - poolSizes[2].descriptorCount = 1; - poolSizes[2].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - - poolSizes[3].descriptorCount = 3; - poolSizes[3].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - - defaultDescriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - defaultDescriptorPoolInfo.pNext = NULL; - defaultDescriptorPoolInfo.flags = 0; - defaultDescriptorPoolInfo.maxSets = 2 + 1 + 1 + 3; - defaultDescriptorPoolInfo.poolSizeCount = 4; - defaultDescriptorPoolInfo.pPoolSizes = poolSizes; - - renderer->vkCreateDescriptorPool( - renderer->logicalDevice, - &defaultDescriptorPoolInfo, - NULL, - &renderer->defaultDescriptorPool - ); - - descriptorAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - descriptorAllocateInfo.pNext = NULL; - descriptorAllocateInfo.descriptorPool = renderer->defaultDescriptorPool; - descriptorAllocateInfo.descriptorSetCount = 1; - descriptorAllocateInfo.pSetLayouts = &renderer->emptyVertexSamplerLayout; - - renderer->vkAllocateDescriptorSets( - renderer->logicalDevice, - &descriptorAllocateInfo, - &renderer->emptyVertexSamplerDescriptorSet - ); - - descriptorAllocateInfo.pSetLayouts = &renderer->emptyFragmentSamplerLayout; - - renderer->vkAllocateDescriptorSets( - renderer->logicalDevice, - &descriptorAllocateInfo, - &renderer->emptyFragmentSamplerDescriptorSet - ); - - descriptorAllocateInfo.pSetLayouts = &renderer->emptyComputeBufferDescriptorSetLayout; - - renderer->vkAllocateDescriptorSets( - renderer->logicalDevice, - &descriptorAllocateInfo, - &renderer->emptyComputeBufferDescriptorSet - ); - - descriptorAllocateInfo.pSetLayouts = &renderer->emptyComputeImageDescriptorSetLayout; - - renderer->vkAllocateDescriptorSets( - renderer->logicalDevice, - &descriptorAllocateInfo, - &renderer->emptyComputeImageDescriptorSet - ); - - /* Dummy Uniform Buffers */ - - renderer->dummyBuffer = VULKAN_INTERNAL_CreateBuffer( - renderer, - 16, - RESOURCE_ACCESS_GENERAL, - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - 0, - 0, - 1 - ); - - renderer->dummyVertexUniformBuffer = VULKAN_INTERNAL_CreateDummyUniformBuffer( - renderer, - UNIFORM_BUFFER_VERTEX - ); - - if (renderer->dummyVertexUniformBuffer == NULL) - { - Refresh_LogError("Failed to create dummy vertex uniform buffer!"); - return NULL; - } - - renderer->dummyFragmentUniformBuffer = VULKAN_INTERNAL_CreateDummyUniformBuffer( - renderer, - UNIFORM_BUFFER_FRAGMENT - ); - - if (renderer->dummyFragmentUniformBuffer == NULL) - { - Refresh_LogError("Failed to create dummy fragment uniform buffer!"); - return NULL; - } - - renderer->dummyComputeUniformBuffer = VULKAN_INTERNAL_CreateDummyUniformBuffer( - renderer, - UNIFORM_BUFFER_COMPUTE - ); - - if (renderer->dummyComputeUniformBuffer == NULL) - { - Refresh_LogError("Failed to create dummy compute uniform buffer!"); - return NULL; - } - - /* Initialize uniform buffer pools */ - - renderer->vertexUniformBufferPool = VULKAN_INTERNAL_CreateUniformBufferPool( - renderer, - UNIFORM_BUFFER_VERTEX - ); - - renderer->fragmentUniformBufferPool = VULKAN_INTERNAL_CreateUniformBufferPool( - renderer, - UNIFORM_BUFFER_FRAGMENT - ); - - renderer->computeUniformBufferPool = VULKAN_INTERNAL_CreateUniformBufferPool( - renderer, - UNIFORM_BUFFER_COMPUTE - ); - - /* Initialize caches */ - - for (i = 0; i < NUM_COMMAND_POOL_BUCKETS; i += 1) - { - renderer->commandPoolHashTable.buckets[i].elements = NULL; - renderer->commandPoolHashTable.buckets[i].count = 0; - renderer->commandPoolHashTable.buckets[i].capacity = 0; - } - - for (i = 0; i < NUM_PIPELINE_LAYOUT_BUCKETS; i += 1) - { - renderer->graphicsPipelineLayoutHashTable.buckets[i].elements = NULL; - renderer->graphicsPipelineLayoutHashTable.buckets[i].count = 0; - renderer->graphicsPipelineLayoutHashTable.buckets[i].capacity = 0; - } - - for (i = 0; i < NUM_PIPELINE_LAYOUT_BUCKETS; i += 1) - { - renderer->computePipelineLayoutHashTable.buckets[i].elements = NULL; - renderer->computePipelineLayoutHashTable.buckets[i].count = 0; - renderer->computePipelineLayoutHashTable.buckets[i].capacity = 0; - } - - for (i = 0; i < NUM_DESCRIPTOR_SET_LAYOUT_BUCKETS; i += 1) - { - renderer->descriptorSetLayoutHashTable.buckets[i].elements = NULL; - renderer->descriptorSetLayoutHashTable.buckets[i].count = 0; - renderer->descriptorSetLayoutHashTable.buckets[i].capacity = 0; - } - - renderer->renderPassHashArray.elements = NULL; - renderer->renderPassHashArray.count = 0; - renderer->renderPassHashArray.capacity = 0; - - renderer->framebufferHashArray.elements = NULL; - renderer->framebufferHashArray.count = 0; - renderer->framebufferHashArray.capacity = 0; - - renderer->renderTargetHashArray.elements = NULL; - renderer->renderTargetHashArray.count = 0; - renderer->renderTargetHashArray.capacity = 0; - - /* Initialize transfer buffer pool */ - - renderer->transferBufferPool.lock = SDL_CreateMutex(); - - renderer->transferBufferPool.availableBufferCapacity = 4; - renderer->transferBufferPool.availableBufferCount = 0; - renderer->transferBufferPool.availableBuffers = SDL_malloc( - renderer->transferBufferPool.availableBufferCapacity * sizeof(VulkanTransferBuffer*) - ); - - for (i = 0; i < renderer->transferBufferPool.availableBufferCapacity; i += 1) - { - transferBuffer = SDL_malloc(sizeof(VulkanTransferBuffer)); - transferBuffer->offset = 0; - transferBuffer->fromPool = 1; - transferBuffer->buffer = VULKAN_INTERNAL_CreateBuffer( - renderer, - POOLED_TRANSFER_BUFFER_SIZE, - RESOURCE_ACCESS_TRANSFER_READ_WRITE, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - 1, - 0, - 1 - ); - - if (transferBuffer->buffer == NULL) - { - Refresh_LogError("Failed to allocate transfer buffer!"); - SDL_free(transferBuffer); - } - - renderer->transferBufferPool.availableBuffers[i] = transferBuffer; - renderer->transferBufferPool.availableBufferCount += 1; - } - - /* Initialize fence pool */ - - renderer->fencePool.lock = SDL_CreateMutex(); - - renderer->fencePool.availableFenceCapacity = 4; - renderer->fencePool.availableFenceCount = 0; - renderer->fencePool.availableFences = SDL_malloc( - renderer->fencePool.availableFenceCapacity * sizeof(VkFence) - ); - - /* Some drivers don't support D16, so we have to fall back to D32. */ - - vulkanResult = renderer->vkGetPhysicalDeviceImageFormatProperties( - renderer->physicalDevice, - VK_FORMAT_D16_UNORM, - VK_IMAGE_TYPE_2D, - VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_ASPECT_DEPTH_BIT, - 0, - &imageFormatProperties - ); - - if (vulkanResult == VK_ERROR_FORMAT_NOT_SUPPORTED) - { - renderer->D16Format = VK_FORMAT_D32_SFLOAT; - } - else - { - renderer->D16Format = VK_FORMAT_D16_UNORM; - } - - vulkanResult = renderer->vkGetPhysicalDeviceImageFormatProperties( - renderer->physicalDevice, - VK_FORMAT_D16_UNORM_S8_UINT, - VK_IMAGE_TYPE_2D, - VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, - 0, - &imageFormatProperties - ); - - if (vulkanResult == VK_ERROR_FORMAT_NOT_SUPPORTED) - { - renderer->D16S8Format = VK_FORMAT_D32_SFLOAT_S8_UINT; - } - else - { - renderer->D16S8Format = VK_FORMAT_D16_UNORM_S8_UINT; - } - - /* Deferred destroy storage */ - - renderer->texturesToDestroyCapacity = 16; - renderer->texturesToDestroyCount = 0; - - renderer->texturesToDestroy = (VulkanTexture**)SDL_malloc( - sizeof(VulkanTexture*) * - renderer->texturesToDestroyCapacity - ); - - renderer->buffersToDestroyCapacity = 16; - renderer->buffersToDestroyCount = 0; - - renderer->buffersToDestroy = SDL_malloc( - sizeof(VulkanBuffer*) * - renderer->buffersToDestroyCapacity - ); - - renderer->samplersToDestroyCapacity = 16; - renderer->samplersToDestroyCount = 0; - - renderer->samplersToDestroy = SDL_malloc( - sizeof(VulkanSampler*) * - renderer->samplersToDestroyCapacity - ); - - renderer->graphicsPipelinesToDestroyCapacity = 16; - renderer->graphicsPipelinesToDestroyCount = 0; - - renderer->graphicsPipelinesToDestroy = SDL_malloc( - sizeof(VulkanGraphicsPipeline*) * - renderer->graphicsPipelinesToDestroyCapacity - ); - - renderer->computePipelinesToDestroyCapacity = 16; - renderer->computePipelinesToDestroyCount = 0; - - renderer->computePipelinesToDestroy = SDL_malloc( - sizeof(VulkanComputePipeline*) * - renderer->computePipelinesToDestroyCapacity - ); - - renderer->shaderModulesToDestroyCapacity = 16; - renderer->shaderModulesToDestroyCount = 0; - - renderer->shaderModulesToDestroy = SDL_malloc( - sizeof(VulkanShaderModule*) * - renderer->shaderModulesToDestroyCapacity - ); - - renderer->framebuffersToDestroyCapacity = 16; - renderer->framebuffersToDestroyCount = 0; - renderer->framebuffersToDestroy = SDL_malloc( - sizeof(VulkanFramebuffer*) * - renderer->framebuffersToDestroyCapacity - ); - - renderer->needDefrag = 0; - renderer->defragTimestamp = 0; - renderer->defragInProgress = 0; - - return result; -} - -Refresh_Driver VulkanDriver = { - "Vulkan", - VULKAN_PrepareDriver, - VULKAN_CreateDevice -}; - -#endif //REFRESH_DRIVER_VULKAN diff --git a/src/Refresh_driver.h b/src/Refresh_driver.h new file mode 100644 index 0000000..74177f0 --- /dev/null +++ b/src/Refresh_driver.h @@ -0,0 +1,818 @@ +/* Refresh - a cross-platform hardware-accelerated graphics library with modern capabilities + * + * Copyright (c) 2020-2024 Evan Hemsley + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Evan "cosmonaut" Hemsley + * + */ + +#ifndef REFRESH_DRIVER_H +#define REFRESH_DRIVER_H + +#include "Refresh.h" + +/* Common Struct */ + +typedef struct Pass +{ + Refresh_CommandBuffer *commandBuffer; + SDL_bool inProgress; +} Pass; + +typedef struct CommandBufferCommonHeader +{ + Refresh_Device *device; + Pass renderPass; + SDL_bool graphicsPipelineBound; + Pass computePass; + SDL_bool computePipelineBound; + Pass copyPass; + SDL_bool submitted; +} CommandBufferCommonHeader; + +/* Internal Helper Utilities */ + +static inline Sint32 Texture_GetBlockSize( + Refresh_TextureFormat format +) { + switch (format) + { + case REFRESH_TEXTUREFORMAT_BC1: + case REFRESH_TEXTUREFORMAT_BC2: + case REFRESH_TEXTUREFORMAT_BC3: + case REFRESH_TEXTUREFORMAT_BC7: + case REFRESH_TEXTUREFORMAT_BC3_SRGB: + case REFRESH_TEXTUREFORMAT_BC7_SRGB: + return 4; + case REFRESH_TEXTUREFORMAT_R8: + case REFRESH_TEXTUREFORMAT_A8: + case REFRESH_TEXTUREFORMAT_R8_UINT: + case REFRESH_TEXTUREFORMAT_R5G6B5: + case REFRESH_TEXTUREFORMAT_B4G4R4A4: + case REFRESH_TEXTUREFORMAT_A1R5G5B5: + case REFRESH_TEXTUREFORMAT_R16_SFLOAT: + case REFRESH_TEXTUREFORMAT_R8G8_SNORM: + case REFRESH_TEXTUREFORMAT_R8G8_UINT: + case REFRESH_TEXTUREFORMAT_R16_UINT: + case REFRESH_TEXTUREFORMAT_R8G8B8A8: + case REFRESH_TEXTUREFORMAT_R32_SFLOAT: + case REFRESH_TEXTUREFORMAT_R16G16_SFLOAT: + case REFRESH_TEXTUREFORMAT_R8G8B8A8_SNORM: + case REFRESH_TEXTUREFORMAT_R8G8B8A8_SRGB: + case REFRESH_TEXTUREFORMAT_B8G8R8A8_SRGB: + case REFRESH_TEXTUREFORMAT_A2R10G10B10: + case REFRESH_TEXTUREFORMAT_R8G8B8A8_UINT: + case REFRESH_TEXTUREFORMAT_R16G16_UINT: + case REFRESH_TEXTUREFORMAT_R16G16B16A16_SFLOAT: + case REFRESH_TEXTUREFORMAT_R16G16B16A16: + case REFRESH_TEXTUREFORMAT_R32G32_SFLOAT: + case REFRESH_TEXTUREFORMAT_R16G16B16A16_UINT: + case REFRESH_TEXTUREFORMAT_R32G32B32A32_SFLOAT: + return 1; + default: + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "Unrecognized TextureFormat!" + ); + return 0; + } +} + +static inline SDL_bool IsDepthFormat( + Refresh_TextureFormat format +) { + switch (format) + { + case REFRESH_TEXTUREFORMAT_D16_UNORM: + case REFRESH_TEXTUREFORMAT_D24_UNORM: + case REFRESH_TEXTUREFORMAT_D32_SFLOAT: + case REFRESH_TEXTUREFORMAT_D24_UNORM_S8_UINT: + case REFRESH_TEXTUREFORMAT_D32_SFLOAT_S8_UINT: + return SDL_TRUE; + + default: + return SDL_FALSE; + } +} + +static inline SDL_bool IsStencilFormat( + Refresh_TextureFormat format +) { + switch (format) + { + case REFRESH_TEXTUREFORMAT_D24_UNORM_S8_UINT: + case REFRESH_TEXTUREFORMAT_D32_SFLOAT_S8_UINT: + return SDL_TRUE; + + default: + return SDL_FALSE; + } +} + +static inline Uint32 PrimitiveVerts( + Refresh_PrimitiveType primitiveType, + Uint32 primitiveCount +) { + switch (primitiveType) + { + case REFRESH_PRIMITIVETYPE_TRIANGLELIST: + return primitiveCount * 3; + case REFRESH_PRIMITIVETYPE_TRIANGLESTRIP: + return primitiveCount + 2; + case REFRESH_PRIMITIVETYPE_LINELIST: + return primitiveCount * 2; + case REFRESH_PRIMITIVETYPE_LINESTRIP: + return primitiveCount + 1; + case REFRESH_PRIMITIVETYPE_POINTLIST: + return primitiveCount; + default: + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "Unrecognized primitive type!" + ); + return 0; + } +} + +static inline Uint32 IndexSize(Refresh_IndexElementSize size) +{ + return (size == REFRESH_INDEXELEMENTSIZE_16BIT) ? 2 : 4; +} + +static inline Uint32 BytesPerRow( + Sint32 width, + Refresh_TextureFormat format +) { + Uint32 blocksPerRow = width; + + if ( format == REFRESH_TEXTUREFORMAT_BC1 || + format == REFRESH_TEXTUREFORMAT_BC2 || + format == REFRESH_TEXTUREFORMAT_BC3 || + format == REFRESH_TEXTUREFORMAT_BC7 ) + { + blocksPerRow = (width + 3) / 4; + } + + return blocksPerRow * Refresh_TextureFormatTexelBlockSize(format); +} + +static inline Sint32 BytesPerImage( + Uint32 width, + Uint32 height, + Refresh_TextureFormat format +) { + Uint32 blocksPerRow = width; + Uint32 blocksPerColumn = height; + + if ( format == REFRESH_TEXTUREFORMAT_BC1 || + format == REFRESH_TEXTUREFORMAT_BC2 || + format == REFRESH_TEXTUREFORMAT_BC3 || + format == REFRESH_TEXTUREFORMAT_BC7 ) + { + blocksPerRow = (width + 3) / 4; + blocksPerColumn = (height + 3) / 4; + } + + return blocksPerRow * blocksPerColumn * Refresh_TextureFormatTexelBlockSize(format); +} + +/* GraphicsDevice Limits */ + +#define MAX_TEXTURE_SAMPLERS_PER_STAGE 16 +#define MAX_STORAGE_TEXTURES_PER_STAGE 8 +#define MAX_STORAGE_BUFFERS_PER_STAGE 8 +#define MAX_UNIFORM_BUFFERS_PER_STAGE 14 +#define MAX_BUFFER_BINDINGS 16 +#define MAX_COLOR_TARGET_BINDINGS 4 +#define MAX_PRESENT_COUNT 16 +#define MAX_FRAMES_IN_FLIGHT 3 + +/* Refresh_Device Definition */ + +typedef struct Refresh_Renderer Refresh_Renderer; + +struct Refresh_Device +{ + /* Quit */ + + void (*DestroyDevice)(Refresh_Device *device); + + /* State Creation */ + + Refresh_ComputePipeline* (*CreateComputePipeline)( + Refresh_Renderer *driverData, + Refresh_ComputePipelineCreateInfo *pipelineCreateInfo + ); + + Refresh_GraphicsPipeline* (*CreateGraphicsPipeline)( + Refresh_Renderer *driverData, + Refresh_GraphicsPipelineCreateInfo *pipelineCreateInfo + ); + + Refresh_Sampler* (*CreateSampler)( + Refresh_Renderer *driverData, + Refresh_SamplerCreateInfo *samplerCreateInfo + ); + + Refresh_Shader* (*CreateShader)( + Refresh_Renderer *driverData, + Refresh_ShaderCreateInfo *shaderCreateInfo + ); + + Refresh_Texture* (*CreateTexture)( + Refresh_Renderer *driverData, + Refresh_TextureCreateInfo *textureCreateInfo + ); + + Refresh_Buffer* (*CreateBuffer)( + Refresh_Renderer *driverData, + Refresh_BufferUsageFlags usageFlags, + Uint32 sizeInBytes + ); + + Refresh_TransferBuffer* (*CreateTransferBuffer)( + Refresh_Renderer *driverData, + Refresh_TransferUsage usage, + Refresh_TransferBufferMapFlags mapFlags, + Uint32 sizeInBytes + ); + + Refresh_OcclusionQuery* (*CreateOcclusionQuery)( + Refresh_Renderer *driverData + ); + + /* Debug Naming */ + + void (*SetBufferName)( + Refresh_Renderer *driverData, + Refresh_Buffer *buffer, + const char *text + ); + + void (*SetTextureName)( + Refresh_Renderer *driverData, + Refresh_Texture *texture, + const char *text + ); + + void (*SetStringMarker)( + Refresh_CommandBuffer *commandBuffer, + const char *text + ); + + /* Disposal */ + + void (*ReleaseTexture)( + Refresh_Renderer *driverData, + Refresh_Texture *texture + ); + + void (*ReleaseSampler)( + Refresh_Renderer *driverData, + Refresh_Sampler *sampler + ); + + void (*ReleaseBuffer)( + Refresh_Renderer *driverData, + Refresh_Buffer *buffer + ); + + void (*ReleaseTransferBuffer)( + Refresh_Renderer *driverData, + Refresh_TransferBuffer *transferBuffer + ); + + void (*ReleaseShader)( + Refresh_Renderer *driverData, + Refresh_Shader *shader + ); + + void (*ReleaseComputePipeline)( + Refresh_Renderer *driverData, + Refresh_ComputePipeline *computePipeline + ); + + void (*ReleaseGraphicsPipeline)( + Refresh_Renderer *driverData, + Refresh_GraphicsPipeline *graphicsPipeline + ); + + void (*ReleaseOcclusionQuery)( + Refresh_Renderer *driverData, + Refresh_OcclusionQuery *query + ); + + /* Render Pass */ + + void (*BeginRenderPass)( + Refresh_CommandBuffer *commandBuffer, + Refresh_ColorAttachmentInfo *colorAttachmentInfos, + Uint32 colorAttachmentCount, + Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo + ); + + void (*BindGraphicsPipeline)( + Refresh_CommandBuffer *commandBuffer, + Refresh_GraphicsPipeline *graphicsPipeline + ); + + void (*SetViewport)( + Refresh_CommandBuffer *commandBuffer, + Refresh_Viewport *viewport + ); + + void (*SetScissor)( + Refresh_CommandBuffer *commandBuffer, + Refresh_Rect *scissor + ); + + void (*BindVertexBuffers)( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstBinding, + Refresh_BufferBinding *pBindings, + Uint32 bindingCount + ); + + void (*BindIndexBuffer)( + Refresh_CommandBuffer *commandBuffer, + Refresh_BufferBinding *pBinding, + Refresh_IndexElementSize indexElementSize + ); + + void (*BindVertexSamplers)( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSamplerBinding *textureSamplerBindings, + Uint32 bindingCount + ); + + void (*BindVertexStorageTextures)( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount + ); + + void (*BindVertexStorageBuffers)( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount + ); + + void (*BindFragmentSamplers)( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSamplerBinding *textureSamplerBindings, + Uint32 bindingCount + ); + + void (*BindFragmentStorageTextures)( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount + ); + + void (*BindFragmentStorageBuffers)( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount + ); + + void (*PushVertexUniformData)( + Refresh_CommandBuffer *commandBuffer, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes + ); + + void (*PushFragmentUniformData)( + Refresh_CommandBuffer *commandBuffer, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes + ); + + void (*DrawIndexedPrimitives)( + Refresh_CommandBuffer *commandBuffer, + Uint32 baseVertex, + Uint32 startIndex, + Uint32 primitiveCount, + Uint32 instanceCount + ); + + void (*DrawPrimitives)( + Refresh_CommandBuffer *commandBuffer, + Uint32 vertexStart, + Uint32 primitiveCount + ); + + void (*DrawPrimitivesIndirect)( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *buffer, + Uint32 offsetInBytes, + Uint32 drawCount, + Uint32 stride + ); + + void (*DrawIndexedPrimitivesIndirect)( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *buffer, + Uint32 offsetInBytes, + Uint32 drawCount, + Uint32 stride + ); + + void (*EndRenderPass)( + Refresh_CommandBuffer *commandBuffer + ); + + /* Compute Pass */ + + void (*BeginComputePass)( + Refresh_CommandBuffer *commandBuffer, + Refresh_StorageTextureReadWriteBinding *storageTextureBindings, + Uint32 storageTextureBindingCount, + Refresh_StorageBufferReadWriteBinding *storageBufferBindings, + Uint32 storageBufferBindingCount + ); + + void (*BindComputePipeline)( + Refresh_CommandBuffer *commandBuffer, + Refresh_ComputePipeline *computePipeline + ); + + void (*BindComputeStorageTextures)( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount + ); + + void (*BindComputeStorageBuffers)( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount + ); + + void (*PushComputeUniformData)( + Refresh_CommandBuffer *commandBuffer, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes + ); + + void (*DispatchCompute)( + Refresh_CommandBuffer *commandBuffer, + Uint32 groupCountX, + Uint32 groupCountY, + Uint32 groupCountZ + ); + + void (*EndComputePass)( + Refresh_CommandBuffer *commandBuffer + ); + + /* TransferBuffer Data */ + + void (*MapTransferBuffer)( + Refresh_Renderer *device, + Refresh_TransferBuffer *transferBuffer, + SDL_bool cycle, + void **ppData + ); + + void (*UnmapTransferBuffer)( + Refresh_Renderer *device, + Refresh_TransferBuffer *transferBuffer + ); + + void (*SetTransferData)( + Refresh_Renderer *driverData, + void* data, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams, + SDL_bool cycle + ); + + void (*GetTransferData)( + Refresh_Renderer *driverData, + Refresh_TransferBuffer *transferBuffer, + void* data, + Refresh_BufferCopy *copyParams + ); + + /* Copy Pass */ + + void (*BeginCopyPass)( + Refresh_CommandBuffer *commandBuffer + ); + + void (*UploadToTexture)( + Refresh_CommandBuffer *commandBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_TextureRegion *textureSlice, + Refresh_BufferImageCopy *copyParams, + SDL_bool cycle + ); + + void (*UploadToBuffer)( + Refresh_CommandBuffer *commandBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_Buffer *buffer, + Refresh_BufferCopy *copyParams, + SDL_bool cycle + ); + + void (*CopyTextureToTexture)( + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *source, + Refresh_TextureRegion *destination, + SDL_bool cycle + ); + + void (*CopyBufferToBuffer)( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *source, + Refresh_Buffer *destination, + Refresh_BufferCopy *copyParams, + SDL_bool cycle + ); + + void (*GenerateMipmaps)( + Refresh_CommandBuffer *commandBuffer, + Refresh_Texture *texture + ); + + void (*DownloadFromTexture)( + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *textureSlice, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferImageCopy *copyParams + ); + + void (*DownloadFromBuffer)( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *buffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams + ); + + void (*EndCopyPass)( + Refresh_CommandBuffer *commandBuffer + ); + + void (*Blit)( + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *source, + Refresh_TextureRegion *destination, + Refresh_Filter filterMode, + SDL_bool cycle + ); + + /* Submission/Presentation */ + + SDL_bool (*SupportsSwapchainComposition)( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition + ); + + SDL_bool (*SupportsPresentMode)( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_PresentMode presentMode + ); + + SDL_bool (*ClaimWindow)( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition, + Refresh_PresentMode presentMode + ); + + void (*UnclaimWindow)( + Refresh_Renderer *driverData, + SDL_Window *window + ); + + void (*SetSwapchainParameters)( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition, + Refresh_PresentMode presentMode + ); + + Refresh_TextureFormat (*GetSwapchainTextureFormat)( + Refresh_Renderer *driverData, + SDL_Window *window + ); + + Refresh_CommandBuffer* (*AcquireCommandBuffer)( + Refresh_Renderer *driverData + ); + + Refresh_Texture* (*AcquireSwapchainTexture)( + Refresh_CommandBuffer *commandBuffer, + SDL_Window *window, + Uint32 *pWidth, + Uint32 *pHeight + ); + + void (*Submit)( + Refresh_CommandBuffer *commandBuffer + ); + + Refresh_Fence* (*SubmitAndAcquireFence)( + Refresh_CommandBuffer *commandBuffer + ); + + void (*Wait)( + Refresh_Renderer *driverData + ); + + void (*WaitForFences)( + Refresh_Renderer *driverData, + SDL_bool waitAll, + Uint32 fenceCount, + Refresh_Fence **pFences + ); + + SDL_bool (*QueryFence)( + Refresh_Renderer *driverData, + Refresh_Fence *fence + ); + + void (*ReleaseFence)( + Refresh_Renderer *driverData, + Refresh_Fence *fence + ); + + /* Queries */ + + void (*OcclusionQueryBegin)( + Refresh_CommandBuffer *commandBuffer, + Refresh_OcclusionQuery *query + ); + + void (*OcclusionQueryEnd)( + Refresh_CommandBuffer *commandBuffer, + Refresh_OcclusionQuery *query + ); + + SDL_bool (*OcclusionQueryPixelCount)( + Refresh_Renderer *driverData, + Refresh_OcclusionQuery *query, + Uint32 *pixelCount + ); + + /* Feature Queries */ + + SDL_bool (*IsTextureFormatSupported)( + Refresh_Renderer *driverData, + Refresh_TextureFormat format, + Refresh_TextureType type, + Refresh_TextureUsageFlags usage + ); + + Refresh_SampleCount (*GetBestSampleCount)( + Refresh_Renderer *driverData, + Refresh_TextureFormat format, + Refresh_SampleCount desiredSampleCount + ); + + /* SPIR-V Cross Interop */ + + Refresh_Shader* (*CompileFromSPIRVCross)( + Refresh_Renderer *driverData, + Refresh_ShaderStage shader_stage, + const char *entryPointName, + const char *source + ); + + /* Opaque pointer for the Driver */ + Refresh_Renderer *driverData; + + /* Store this for Refresh_GetBackend() */ + Refresh_Backend backend; +}; + +#define ASSIGN_DRIVER_FUNC(func, name) \ + result->func = name##_##func; +#define ASSIGN_DRIVER(name) \ + ASSIGN_DRIVER_FUNC(DestroyDevice, name) \ + ASSIGN_DRIVER_FUNC(CreateComputePipeline, name) \ + ASSIGN_DRIVER_FUNC(CreateGraphicsPipeline, name) \ + ASSIGN_DRIVER_FUNC(CreateSampler, name) \ + ASSIGN_DRIVER_FUNC(CreateShader, name) \ + ASSIGN_DRIVER_FUNC(CreateTexture, name) \ + ASSIGN_DRIVER_FUNC(CreateBuffer, name) \ + ASSIGN_DRIVER_FUNC(CreateTransferBuffer, name) \ + ASSIGN_DRIVER_FUNC(CreateOcclusionQuery, name) \ + ASSIGN_DRIVER_FUNC(SetBufferName, name) \ + ASSIGN_DRIVER_FUNC(SetTextureName, name) \ + ASSIGN_DRIVER_FUNC(SetStringMarker, name) \ + ASSIGN_DRIVER_FUNC(ReleaseTexture, name) \ + ASSIGN_DRIVER_FUNC(ReleaseSampler, name) \ + ASSIGN_DRIVER_FUNC(ReleaseBuffer, name) \ + ASSIGN_DRIVER_FUNC(ReleaseTransferBuffer, name) \ + ASSIGN_DRIVER_FUNC(ReleaseShader, name) \ + ASSIGN_DRIVER_FUNC(ReleaseComputePipeline, name) \ + ASSIGN_DRIVER_FUNC(ReleaseGraphicsPipeline, name) \ + ASSIGN_DRIVER_FUNC(ReleaseOcclusionQuery, name) \ + ASSIGN_DRIVER_FUNC(BeginRenderPass, name) \ + ASSIGN_DRIVER_FUNC(BindGraphicsPipeline, name) \ + ASSIGN_DRIVER_FUNC(SetViewport, name) \ + ASSIGN_DRIVER_FUNC(SetScissor, name) \ + ASSIGN_DRIVER_FUNC(BindVertexBuffers, name) \ + ASSIGN_DRIVER_FUNC(BindIndexBuffer, name) \ + ASSIGN_DRIVER_FUNC(BindVertexSamplers, name) \ + ASSIGN_DRIVER_FUNC(BindVertexStorageTextures, name) \ + ASSIGN_DRIVER_FUNC(BindVertexStorageBuffers, name) \ + ASSIGN_DRIVER_FUNC(BindFragmentSamplers, name) \ + ASSIGN_DRIVER_FUNC(BindFragmentStorageTextures, name) \ + ASSIGN_DRIVER_FUNC(BindFragmentStorageBuffers, name) \ + ASSIGN_DRIVER_FUNC(PushVertexUniformData, name) \ + ASSIGN_DRIVER_FUNC(PushFragmentUniformData, name) \ + ASSIGN_DRIVER_FUNC(DrawIndexedPrimitives, name) \ + ASSIGN_DRIVER_FUNC(DrawPrimitives, name) \ + ASSIGN_DRIVER_FUNC(DrawPrimitivesIndirect, name) \ + ASSIGN_DRIVER_FUNC(DrawIndexedPrimitivesIndirect, name) \ + ASSIGN_DRIVER_FUNC(EndRenderPass, name) \ + ASSIGN_DRIVER_FUNC(BeginComputePass, name) \ + ASSIGN_DRIVER_FUNC(BindComputePipeline, name) \ + ASSIGN_DRIVER_FUNC(BindComputeStorageTextures, name) \ + ASSIGN_DRIVER_FUNC(BindComputeStorageBuffers, name) \ + ASSIGN_DRIVER_FUNC(PushComputeUniformData, name) \ + ASSIGN_DRIVER_FUNC(DispatchCompute, name) \ + ASSIGN_DRIVER_FUNC(EndComputePass, name) \ + ASSIGN_DRIVER_FUNC(MapTransferBuffer, name) \ + ASSIGN_DRIVER_FUNC(UnmapTransferBuffer, name) \ + ASSIGN_DRIVER_FUNC(SetTransferData, name) \ + ASSIGN_DRIVER_FUNC(GetTransferData, name) \ + ASSIGN_DRIVER_FUNC(BeginCopyPass, name) \ + ASSIGN_DRIVER_FUNC(UploadToTexture, name) \ + ASSIGN_DRIVER_FUNC(UploadToBuffer, name) \ + ASSIGN_DRIVER_FUNC(DownloadFromTexture, name) \ + ASSIGN_DRIVER_FUNC(DownloadFromBuffer, name) \ + ASSIGN_DRIVER_FUNC(CopyTextureToTexture, name) \ + ASSIGN_DRIVER_FUNC(CopyBufferToBuffer, name) \ + ASSIGN_DRIVER_FUNC(GenerateMipmaps, name) \ + ASSIGN_DRIVER_FUNC(EndCopyPass, name) \ + ASSIGN_DRIVER_FUNC(Blit, name) \ + ASSIGN_DRIVER_FUNC(SupportsSwapchainComposition, name) \ + ASSIGN_DRIVER_FUNC(SupportsPresentMode, name) \ + ASSIGN_DRIVER_FUNC(ClaimWindow, name) \ + ASSIGN_DRIVER_FUNC(UnclaimWindow, name) \ + ASSIGN_DRIVER_FUNC(SetSwapchainParameters, name) \ + ASSIGN_DRIVER_FUNC(GetSwapchainTextureFormat, name) \ + ASSIGN_DRIVER_FUNC(AcquireCommandBuffer, name) \ + ASSIGN_DRIVER_FUNC(AcquireSwapchainTexture, name) \ + ASSIGN_DRIVER_FUNC(Submit, name) \ + ASSIGN_DRIVER_FUNC(SubmitAndAcquireFence, name) \ + ASSIGN_DRIVER_FUNC(Wait, name) \ + ASSIGN_DRIVER_FUNC(WaitForFences, name) \ + ASSIGN_DRIVER_FUNC(QueryFence, name) \ + ASSIGN_DRIVER_FUNC(ReleaseFence, name) \ + ASSIGN_DRIVER_FUNC(OcclusionQueryBegin, name) \ + ASSIGN_DRIVER_FUNC(OcclusionQueryEnd, name) \ + ASSIGN_DRIVER_FUNC(OcclusionQueryPixelCount, name) \ + ASSIGN_DRIVER_FUNC(IsTextureFormatSupported, name) \ + ASSIGN_DRIVER_FUNC(GetBestSampleCount, name) \ + ASSIGN_DRIVER_FUNC(CompileFromSPIRVCross, name) + +typedef struct Refresh_Driver +{ + const char *Name; + const Refresh_Backend backendflag; + SDL_bool (*PrepareDriver)(); + Refresh_Device* (*CreateDevice)(SDL_bool debugMode); +} Refresh_Driver; + +extern Refresh_Driver VulkanDriver; +extern Refresh_Driver D3D11Driver; +extern Refresh_Driver MetalDriver; +extern Refresh_Driver PS5Driver; + +#endif /* REFRESH_DRIVER_H */ diff --git a/src/Refresh_Driver_Template.txt b/src/Refresh_driver_template.txt similarity index 100% rename from src/Refresh_Driver_Template.txt rename to src/Refresh_driver_template.txt diff --git a/src/Refresh_Image.c b/src/Refresh_image.c similarity index 99% rename from src/Refresh_Image.c rename to src/Refresh_image.c index 49f2a00..3ee3e60 100644 --- a/src/Refresh_Image.c +++ b/src/Refresh_image.c @@ -27,7 +27,7 @@ * written by Ethan Lee. */ -#include "Refresh_Image.h" +#include "Refresh_image.h" #include @@ -137,6 +137,7 @@ SDL_SIMDRealloc(void *mem, const size_t len) #define STB_IMAGE_STATIC #define STBI_NO_HDR +#define STBI_NO_GIF #define STBI_ASSERT SDL_assert #define STBI_MALLOC SDL_SIMDAlloc #define STBI_REALLOC SDL_SIMDRealloc diff --git a/src/Refresh_spirv.c b/src/Refresh_spirv.c new file mode 100644 index 0000000..22ba076 --- /dev/null +++ b/src/Refresh_spirv.c @@ -0,0 +1,168 @@ +/* Refresh - a cross-platform hardware-accelerated graphics library with modern capabilities + * + * Copyright (c) 2020-2024 Evan Hemsley + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Evan "cosmonaut" Hemsley + * + */ + +#include "Refresh_driver.h" +#include "Refresh_spirv_c.h" +#include "spirv_cross_c.h" + +#if defined(_WIN32) +#define SPIRV_CROSS_DLL "spirv-cross-c-shared.dll" +#elif defined(__APPLE__) +#define SPIRV_CROSS_DLL "libspirv-cross-c-shared.dylib" +#else +#define SPIRV_CROSS_DLL "libspirv-cross-c-shared.so" +#endif + +#define SPVC_ERROR(func) \ + SDL_SetError(#func " failed: %s", SDL_spvc_context_get_last_error_string(context)) + +static void* spirvcross_dll = NULL; + +typedef spvc_result (*pfn_spvc_context_create)(spvc_context *context); +typedef void (*pfn_spvc_context_destroy)(spvc_context); +typedef spvc_result (*pfn_spvc_context_parse_spirv)(spvc_context, const SpvId*, size_t, spvc_parsed_ir*); +typedef spvc_result (*pfn_spvc_context_create_compiler)(spvc_context, spvc_backend, spvc_parsed_ir, spvc_capture_mode, spvc_compiler*); +typedef spvc_result (*pfn_spvc_compiler_create_compiler_options)(spvc_compiler, spvc_compiler_options*); +typedef spvc_result (*pfn_spvc_compiler_options_set_uint)(spvc_compiler_options, spvc_compiler_option, unsigned); +typedef spvc_result (*pfn_spvc_compiler_install_compiler_options)(spvc_compiler, spvc_compiler_options); +typedef spvc_result (*pfn_spvc_compiler_compile)(spvc_compiler, const char **); +typedef const char *(*pfn_spvc_context_get_last_error_string)(spvc_context); + +static pfn_spvc_context_create SDL_spvc_context_create = NULL; +static pfn_spvc_context_destroy SDL_spvc_context_destroy = NULL; +static pfn_spvc_context_parse_spirv SDL_spvc_context_parse_spirv = NULL; +static pfn_spvc_context_create_compiler SDL_spvc_context_create_compiler = NULL; +static pfn_spvc_compiler_create_compiler_options SDL_spvc_compiler_create_compiler_options = NULL; +static pfn_spvc_compiler_options_set_uint SDL_spvc_compiler_options_set_uint = NULL; +static pfn_spvc_compiler_install_compiler_options SDL_spvc_compiler_install_compiler_options = NULL; +static pfn_spvc_compiler_compile SDL_spvc_compiler_compile = NULL; +static pfn_spvc_context_get_last_error_string SDL_spvc_context_get_last_error_string = NULL; + +Refresh_Shader* SDL_CreateShaderFromSPIRV(Refresh_Device *device, Refresh_ShaderCreateInfo *createInfo) +{ + Refresh_Shader *shader; + spvc_result result; + spvc_backend backend; + spvc_context context = NULL; + spvc_parsed_ir ir = NULL; + spvc_compiler compiler = NULL; + spvc_compiler_options options = NULL; + const char* translated = NULL; + + switch (Refresh_GetBackend(device)) + { + case REFRESH_BACKEND_D3D11: backend = SPVC_BACKEND_HLSL; break; + case REFRESH_BACKEND_METAL: backend = SPVC_BACKEND_MSL; break; + default: + SDL_SetError("SDL_CreateShaderFromSPIRV: Unexpected Refresh_Backend"); + return NULL; + } + + /* FIXME: spirv-cross could probably be loaded in a better spot */ + if (spirvcross_dll == NULL) { + spirvcross_dll = SDL_LoadObject(SPIRV_CROSS_DLL); + if (spirvcross_dll == NULL) { + return NULL; + } + } + + #define CHECK_FUNC(func) \ + if (SDL_##func == NULL) { \ + SDL_##func = (pfn_##func) SDL_LoadFunction(spirvcross_dll, #func); \ + if (SDL_##func == NULL) { \ + return NULL; \ + } \ + } + CHECK_FUNC(spvc_context_create) + CHECK_FUNC(spvc_context_destroy) + CHECK_FUNC(spvc_context_parse_spirv) + CHECK_FUNC(spvc_context_create_compiler) + CHECK_FUNC(spvc_compiler_create_compiler_options) + CHECK_FUNC(spvc_compiler_options_set_uint) + CHECK_FUNC(spvc_compiler_install_compiler_options) + CHECK_FUNC(spvc_compiler_compile) + CHECK_FUNC(spvc_context_get_last_error_string) + #undef CHECK_FUNC + + /* Create the SPIRV-Cross context */ + result = SDL_spvc_context_create(&context); + if (result < 0) { + SDL_SetError("spvc_context_create failed: %X", result); + return NULL; + } + + /* Parse the SPIR-V into IR */ + result = SDL_spvc_context_parse_spirv(context, (const SpvId*) createInfo->code, createInfo->codeSize / sizeof(SpvId), &ir); + if (result < 0) { + SPVC_ERROR(spvc_context_parse_spirv); + SDL_spvc_context_destroy(context); + return NULL; + } + + /* Create the cross-compiler */ + result = SDL_spvc_context_create_compiler(context, backend, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler); + if (result < 0) { + SPVC_ERROR(spvc_context_create_compiler); + SDL_spvc_context_destroy(context); + return NULL; + } + + /* Set up the cross-compiler options */ + result = SDL_spvc_compiler_create_compiler_options(compiler, &options); + if (result < 0) { + SPVC_ERROR(spvc_compiler_create_compiler_options); + SDL_spvc_context_destroy(context); + return NULL; + } + + if (backend == SPVC_BACKEND_HLSL) { + SDL_spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL, 50); + SDL_spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_HLSL_NONWRITABLE_UAV_TEXTURE_AS_SRV, 1); + } + + result = SDL_spvc_compiler_install_compiler_options(compiler, options); + if (result < 0) { + SPVC_ERROR(spvc_compiler_install_compiler_options); + SDL_spvc_context_destroy(context); + return NULL; + } + + /* Compile to the target shader language */ + result = SDL_spvc_compiler_compile(compiler, &translated); + if (result < 0) { + SPVC_ERROR(spvc_compiler_compile); + SDL_spvc_context_destroy(context); + return NULL; + } + + /* Compile the shader */ + shader = device->CompileFromSPIRVCross(device->driverData, createInfo->stage, createInfo->entryPointName, translated); + + /* Clean up */ + SDL_spvc_context_destroy(context); + + return shader; +} diff --git a/src/Refresh_spirv_c.h b/src/Refresh_spirv_c.h new file mode 100644 index 0000000..198497a --- /dev/null +++ b/src/Refresh_spirv_c.h @@ -0,0 +1,31 @@ +/* Refresh - a cross-platform hardware-accelerated graphics library with modern capabilities + * + * Copyright (c) 2020-2024 Evan Hemsley + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Evan "cosmonaut" Hemsley + * + */ +#include + +extern Refresh_Shader* SDL_CreateShaderFromSPIRV( + Refresh_Device *device, + Refresh_ShaderCreateInfo *createInfo +); diff --git a/src/d3d11/Refresh_d3d11.c b/src/d3d11/Refresh_d3d11.c new file mode 100644 index 0000000..842314f --- /dev/null +++ b/src/d3d11/Refresh_d3d11.c @@ -0,0 +1,7049 @@ +/* Refresh - a cross-platform hardware-accelerated graphics library with modern capabilities + * + * Copyright (c) 2020-2024 Evan Hemsley + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Evan "cosmonaut" Hemsley + * + */ + +#if REFRESH_D3D11 + +#define D3D11_NO_HELPERS +#define CINTERFACE +#define COBJMACROS + +#include +#include +#include +#include +#include + +#include "SDL_gpu_driver.h" +#include + +/* MinGW doesn't implement this yet */ +#ifdef _WIN32 +#define HAVE_IDXGIINFOQUEUE +#endif + +/* Function Pointer Signatures */ +typedef HRESULT(WINAPI* PFN_CREATE_DXGI_FACTORY1)(const GUID* riid, void** ppFactory); +typedef HRESULT(WINAPI* PFN_DXGI_GET_DEBUG_INTERFACE)(const GUID* riid, void** ppDebug); + + /* IIDs (from https://magnumdb.com) */ +static const IID D3D_IID_IDXGIFactory1 = { 0x770aae78,0xf26f,0x4dba,{0xa8,0x29,0x25,0x3c,0x83,0xd1,0xb3,0x87} }; +static const IID D3D_IID_IDXGIFactory4 = { 0x1bc6ea02,0xef36,0x464f,{0xbf,0x0c,0x21,0xca,0x39,0xe5,0x16,0x8a} }; +static const IID D3D_IID_IDXGIFactory5 = { 0x7632e1f5,0xee65,0x4dca,{0x87,0xfd,0x84,0xcd,0x75,0xf8,0x83,0x8d} }; +static const IID D3D_IID_IDXGIFactory6 = { 0xc1b6694f,0xff09,0x44a9,{0xb0,0x3c,0x77,0x90,0x0a,0x0a,0x1d,0x17} }; +static const IID D3D_IID_IDXGIAdapter1 = { 0x29038f61,0x3839,0x4626,{0x91,0xfd,0x08,0x68,0x79,0x01,0x1a,0x05} }; +static const IID D3D_IID_IDXGISwapChain3 = { 0x94d99bdb,0xf1f8,0x4ab0,{0xb2,0x36,0x7d,0xa0,0x17,0x0e,0xda,0xb1} }; +static const IID D3D_IID_ID3D11Texture2D = { 0x6f15aaf2,0xd208,0x4e89,{0x9a,0xb4,0x48,0x95,0x35,0xd3,0x4f,0x9c} }; +static const IID D3D_IID_ID3DUserDefinedAnnotation = { 0xb2daad8b,0x03d4,0x4dbf,{0x95,0xeb,0x32,0xab,0x4b,0x63,0xd0,0xab} }; +static const IID D3D_IID_ID3D11Device1 = { 0xa04bfb29,0x08ef,0x43d6,{0xa4,0x9c,0xa9,0xbd,0xbd,0xcb,0xe6,0x86} }; +static const IID D3D_IID_IDXGIDebug = { 0x119e7452,0xde9e,0x40fe,{0x88,0x06,0x88,0xf9,0x0c,0x12,0xb4,0x41} }; +#ifdef HAVE_IDXGIINFOQUEUE +static const IID D3D_IID_IDXGIInfoQueue = { 0xd67441c7,0x672a,0x476f,{0x9e,0x82,0xcd,0x55,0xb4,0x49,0x49,0xce} }; +#endif + +static const GUID D3D_IID_D3DDebugObjectName = { 0x429b8c22, 0x9188, 0x4b0c, { 0x87, 0x42, 0xac, 0xb0, 0xbf, 0x85, 0xc2, 0x00 } }; +static const GUID D3D_IID_DXGI_DEBUG_ALL = { 0xe48ae283,0xda80,0x490b,{0x87,0xe6,0x43,0xe9,0xa9,0xcf,0xda,0x08} }; + + /* Defines */ + +#if defined(_WIN32) +#define D3D11_DLL "d3d11.dll" +#define DXGI_DLL "dxgi.dll" +#define DXGIDEBUG_DLL "dxgidebug.dll" +#elif defined(__APPLE__) +#define D3D11_DLL "libdxvk_d3d11.dylib" +#define DXGI_DLL "libdxvk_dxgi.dylib" +#define DXGIDEBUG_DLL "libdxvk_dxgidebug.dylib" +#else +#define D3D11_DLL "libdxvk_d3d11.so" +#define DXGI_DLL "libdxvk_dxgi.so" +#define DXGIDEBUG_DLL "libdxvk_dxgidebug.so" +#endif + +#define D3D11_CREATE_DEVICE_FUNC "D3D11CreateDevice" +#define CREATE_DXGI_FACTORY1_FUNC "CreateDXGIFactory1" +#define DXGI_GET_DEBUG_INTERFACE_FUNC "DXGIGetDebugInterface" +#define WINDOW_PROPERTY_DATA "Refresh_D3D11WindowPropertyData" + +#define UNIFORM_BUFFER_SIZE 1048576 /* 1 MiB */ + +#ifdef _WIN32 +#define HRESULT_FMT "(0x%08lX)" +#else +#define HRESULT_FMT "(0x%08X)" +#endif + +/* Built-in shaders, compiled with compile_shaders.bat */ + +#define g_main D3D11_BlitFrom2D +#include "D3D11_BlitFrom2D.h" +#undef g_main + +#define g_main D3D11_BlitFrom2DArray +#include "D3D11_BlitFrom2DArray.h" +#undef g_main + +#define g_main D3D11_FullscreenVert +#include "D3D11_FullscreenVert.h" +#undef g_main + +/* Macros */ + +#define ERROR_CHECK(msg) \ + if (FAILED(res)) \ + { \ + D3D11_INTERNAL_LogError(renderer->device, msg, res); \ + } + +#define ERROR_CHECK_RETURN(msg, ret) \ + if (FAILED(res)) \ + { \ + D3D11_INTERNAL_LogError(renderer->device, msg, res); \ + return ret; \ + } + +#define EXPAND_ARRAY_IF_NEEDED(arr, elementType, newCount, capacity, newCapacity) \ + if (newCount >= capacity) \ + { \ + capacity = newCapacity; \ + arr = (elementType*) SDL_realloc( \ + arr, \ + sizeof(elementType) * capacity \ + ); \ + } + +#define TRACK_RESOURCE(resource, type, array, count, capacity) \ + Uint32 i; \ + \ + for (i = 0; i < commandBuffer->count; i += 1) \ + { \ + if (commandBuffer->array[i] == resource) \ + { \ + return; \ + } \ + } \ + \ + if (commandBuffer->count == commandBuffer->capacity) \ + { \ + commandBuffer->capacity += 1; \ + commandBuffer->array = SDL_realloc( \ + commandBuffer->array, \ + commandBuffer->capacity * sizeof(type) \ + ); \ + } \ + commandBuffer->array[commandBuffer->count] = resource; \ + commandBuffer->count += 1; \ + SDL_AtomicIncRef(&resource->referenceCount); + +/* Forward Declarations */ + +static void D3D11_Wait(Refresh_Renderer *driverData); +static void D3D11_UnclaimWindow( + Refresh_Renderer * driverData, + SDL_Window *window +); +static void D3D11_INTERNAL_DestroyBlitPipelines(Refresh_Renderer *driverData); + + /* Conversions */ + +static DXGI_FORMAT SwapchainCompositionToTextureFormat[] = +{ + DXGI_FORMAT_B8G8R8A8_UNORM, /* SDR */ + DXGI_FORMAT_B8G8R8A8_UNORM, /* SDR_SRGB */ /* NOTE: The RTV uses the sRGB format */ + DXGI_FORMAT_R16G16B16A16_FLOAT, /* HDR */ + DXGI_FORMAT_R10G10B10A2_UNORM, /* HDR_ADVANCED*/ +}; + +static DXGI_COLOR_SPACE_TYPE SwapchainCompositionToColorSpace[] = +{ + DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, /* SDR */ + DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, /* SDR_SRGB */ + DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, /* HDR */ + DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 /* HDR_ADVANCED */ +}; + +static DXGI_FORMAT RefreshToD3D11_TextureFormat[] = +{ + DXGI_FORMAT_R8G8B8A8_UNORM, /* R8G8B8A8 */ + DXGI_FORMAT_B8G8R8A8_UNORM, /* B8G8R8A8 */ + DXGI_FORMAT_B5G6R5_UNORM, /* R5G6B5 */ /* FIXME: Swizzle? */ + DXGI_FORMAT_B5G5R5A1_UNORM, /* A1R5G5B5 */ /* FIXME: Swizzle? */ + DXGI_FORMAT_B4G4R4A4_UNORM, /* B4G4R4A4 */ + DXGI_FORMAT_R10G10B10A2_UNORM, /* A2R10G10B10 */ + DXGI_FORMAT_UNKNOWN, /* A2B10G10R10 */ /* UNSUPPORTED BY D3D11 */ + DXGI_FORMAT_R16G16_UNORM, /* R16G16 */ + DXGI_FORMAT_R16G16B16A16_UNORM, /* R16G16B16A16 */ + DXGI_FORMAT_R8_UNORM, /* R8 */ + DXGI_FORMAT_A8_UNORM, /* A8 */ + DXGI_FORMAT_BC1_UNORM, /* BC1 */ + DXGI_FORMAT_BC2_UNORM, /* BC2 */ + DXGI_FORMAT_BC3_UNORM, /* BC3 */ + DXGI_FORMAT_BC7_UNORM, /* BC7 */ + DXGI_FORMAT_R8G8_SNORM, /* R8G8_SNORM */ + DXGI_FORMAT_R8G8B8A8_SNORM, /* R8G8B8A8_SNORM */ + DXGI_FORMAT_R16_FLOAT, /* R16_SFLOAT */ + DXGI_FORMAT_R16G16_FLOAT, /* R16G16_SFLOAT */ + DXGI_FORMAT_R16G16B16A16_FLOAT, /* R16G16B16A16_SFLOAT */ + DXGI_FORMAT_R32_FLOAT, /* R32_SFLOAT */ + DXGI_FORMAT_R32G32_FLOAT, /* R32G32_SFLOAT */ + DXGI_FORMAT_R32G32B32A32_FLOAT, /* R32G32B32A32_SFLOAT */ + DXGI_FORMAT_R8_UINT, /* R8_UINT */ + DXGI_FORMAT_R8G8_UINT, /* R8G8_UINT */ + DXGI_FORMAT_R8G8B8A8_UINT, /* R8G8B8A8_UINT */ + DXGI_FORMAT_R16_UINT, /* R16_UINT */ + DXGI_FORMAT_R16G16_UINT, /* R16G16_UINT */ + DXGI_FORMAT_R16G16B16A16_UINT, /* R16G16B16A16_UINT */ + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, /* R8G8B8A8_SRGB */ + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, /* B8G8R8A8_SRGB */ + DXGI_FORMAT_BC3_UNORM_SRGB, /* BC3_SRGB */ + DXGI_FORMAT_BC7_UNORM_SRGB, /* BC7_SRGB */ + DXGI_FORMAT_D16_UNORM, /* D16_UNORM */ + DXGI_FORMAT_D24_UNORM_S8_UINT, /* D24_UNORM */ + DXGI_FORMAT_D32_FLOAT, /* D32_SFLOAT */ + DXGI_FORMAT_D24_UNORM_S8_UINT, /* D24_UNORM_S8_UINT */ + DXGI_FORMAT_D32_FLOAT_S8X24_UINT, /* D32_SFLOAT_S8_UINT */ +}; + +static DXGI_FORMAT RefreshToD3D11_VertexFormat[] = +{ + DXGI_FORMAT_R32_UINT, /* UINT */ + DXGI_FORMAT_R32_FLOAT, /* FLOAT */ + DXGI_FORMAT_R32G32_FLOAT, /* VECTOR2 */ + DXGI_FORMAT_R32G32B32_FLOAT, /* VECTOR3 */ + DXGI_FORMAT_R32G32B32A32_FLOAT, /* VECTOR4 */ + DXGI_FORMAT_R8G8B8A8_UNORM, /* COLOR */ + DXGI_FORMAT_R8G8B8A8_UINT, /* BYTE4 */ + DXGI_FORMAT_R16G16_SINT, /* SHORT2 */ + DXGI_FORMAT_R16G16B16A16_SINT, /* SHORT4 */ + DXGI_FORMAT_R16G16_SNORM, /* NORMALIZEDSHORT2 */ + DXGI_FORMAT_R16G16B16A16_SNORM, /* NORMALIZEDSHORT4 */ + DXGI_FORMAT_R16G16_FLOAT, /* HALFVECTOR2 */ + DXGI_FORMAT_R16G16B16A16_FLOAT /* HALFVECTOR4 */ +}; + +static Uint32 RefreshToD3D11_SampleCount[] = +{ + 1, /* REFRESH_SAMPLECOUNT_1 */ + 2, /* REFRESH_SAMPLECOUNT_2 */ + 4, /* REFRESH_SAMPLECOUNT_4 */ + 8 /* REFRESH_SAMPLECOUNT_8 */ +}; + +static DXGI_FORMAT RefreshToD3D11_IndexType[] = +{ + DXGI_FORMAT_R16_UINT, /* 16BIT */ + DXGI_FORMAT_R32_UINT /* 32BIT */ +}; + +static D3D11_PRIMITIVE_TOPOLOGY RefreshToD3D11_PrimitiveType[] = +{ + D3D_PRIMITIVE_TOPOLOGY_POINTLIST, /* POINTLIST */ + D3D_PRIMITIVE_TOPOLOGY_LINELIST, /* LINELIST */ + D3D_PRIMITIVE_TOPOLOGY_LINESTRIP, /* LINESTRIP */ + D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, /* TRIANGLELIST */ + D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP /* TRIANGLESTRIP */ +}; + +static D3D11_CULL_MODE RefreshToD3D11_CullMode[] = +{ + D3D11_CULL_NONE, /* NONE */ + D3D11_CULL_FRONT, /* FRONT */ + D3D11_CULL_BACK /* BACK */ +}; + +static D3D11_BLEND RefreshToD3D11_BlendFactor[] = +{ + D3D11_BLEND_ZERO, /* ZERO */ + D3D11_BLEND_ONE, /* ONE */ + D3D11_BLEND_SRC_COLOR, /* SRC_COLOR */ + D3D11_BLEND_INV_SRC_COLOR, /* ONE_MINUS_SRC_COLOR */ + D3D11_BLEND_DEST_COLOR, /* DST_COLOR */ + D3D11_BLEND_INV_DEST_COLOR, /* ONE_MINUS_DST_COLOR */ + D3D11_BLEND_SRC_ALPHA, /* SRC_ALPHA */ + D3D11_BLEND_INV_SRC_ALPHA, /* ONE_MINUS_SRC_ALPHA */ + D3D11_BLEND_DEST_ALPHA, /* DST_ALPHA */ + D3D11_BLEND_INV_DEST_ALPHA, /* ONE_MINUS_DST_ALPHA */ + D3D11_BLEND_BLEND_FACTOR, /* CONSTANT_COLOR */ + D3D11_BLEND_INV_BLEND_FACTOR, /* ONE_MINUS_CONSTANT_COLOR */ + D3D11_BLEND_SRC_ALPHA_SAT, /* SRC_ALPHA_SATURATE */ +}; + +static D3D11_BLEND RefreshToD3D11_BlendFactorAlpha[] = +{ + D3D11_BLEND_ZERO, /* ZERO */ + D3D11_BLEND_ONE, /* ONE */ + D3D11_BLEND_SRC_ALPHA, /* SRC_COLOR */ + D3D11_BLEND_INV_SRC_ALPHA, /* ONE_MINUS_SRC_COLOR */ + D3D11_BLEND_DEST_ALPHA, /* DST_COLOR */ + D3D11_BLEND_INV_DEST_ALPHA, /* ONE_MINUS_DST_COLOR */ + D3D11_BLEND_SRC_ALPHA, /* SRC_ALPHA */ + D3D11_BLEND_INV_SRC_ALPHA, /* ONE_MINUS_SRC_ALPHA */ + D3D11_BLEND_DEST_ALPHA, /* DST_ALPHA */ + D3D11_BLEND_INV_DEST_ALPHA, /* ONE_MINUS_DST_ALPHA */ + D3D11_BLEND_BLEND_FACTOR, /* CONSTANT_COLOR */ + D3D11_BLEND_INV_BLEND_FACTOR, /* ONE_MINUS_CONSTANT_COLOR */ + D3D11_BLEND_SRC_ALPHA_SAT, /* SRC_ALPHA_SATURATE */ +}; + +static D3D11_BLEND_OP RefreshToD3D11_BlendOp[] = +{ + D3D11_BLEND_OP_ADD, /* ADD */ + D3D11_BLEND_OP_SUBTRACT, /* SUBTRACT */ + D3D11_BLEND_OP_REV_SUBTRACT, /* REVERSE_SUBTRACT */ + D3D11_BLEND_OP_MIN, /* MIN */ + D3D11_BLEND_OP_MAX /* MAX */ +}; + +static D3D11_COMPARISON_FUNC RefreshToD3D11_CompareOp[] = +{ + D3D11_COMPARISON_NEVER, /* NEVER */ + D3D11_COMPARISON_LESS, /* LESS */ + D3D11_COMPARISON_EQUAL, /* EQUAL */ + D3D11_COMPARISON_LESS_EQUAL, /* LESS_OR_EQUAL */ + D3D11_COMPARISON_GREATER, /* GREATER */ + D3D11_COMPARISON_NOT_EQUAL, /* NOT_EQUAL */ + D3D11_COMPARISON_GREATER_EQUAL, /* GREATER_OR_EQUAL */ + D3D11_COMPARISON_ALWAYS /* ALWAYS */ +}; + +static D3D11_STENCIL_OP RefreshToD3D11_StencilOp[] = +{ + D3D11_STENCIL_OP_KEEP, /* KEEP */ + D3D11_STENCIL_OP_ZERO, /* ZERO */ + D3D11_STENCIL_OP_REPLACE, /* REPLACE */ + D3D11_STENCIL_OP_INCR_SAT, /* INCREMENT_AND_CLAMP */ + D3D11_STENCIL_OP_DECR_SAT, /* DECREMENT_AND_CLAMP */ + D3D11_STENCIL_OP_INVERT, /* INVERT */ + D3D11_STENCIL_OP_INCR, /* INCREMENT_AND_WRAP */ + D3D11_STENCIL_OP_DECR /* DECREMENT_AND_WRAP */ +}; + +static D3D11_INPUT_CLASSIFICATION RefreshToD3D11_VertexInputRate[] = +{ + D3D11_INPUT_PER_VERTEX_DATA, /* VERTEX */ + D3D11_INPUT_PER_INSTANCE_DATA /* INSTANCE */ +}; + +static D3D11_TEXTURE_ADDRESS_MODE RefreshToD3D11_SamplerAddressMode[] = +{ + D3D11_TEXTURE_ADDRESS_WRAP, /* REPEAT */ + D3D11_TEXTURE_ADDRESS_MIRROR, /* MIRRORED_REPEAT */ + D3D11_TEXTURE_ADDRESS_CLAMP, /* CLAMP_TO_EDGE */ + D3D11_TEXTURE_ADDRESS_BORDER /* CLAMP_TO_BORDER */ +}; + +static void RefreshToD3D11_BorderColor( + Refresh_SamplerCreateInfo *createInfo, + D3D11_SAMPLER_DESC *desc +) { + switch (createInfo->borderColor) + { + case REFRESH_BORDERCOLOR_FLOAT_OPAQUE_BLACK: + case REFRESH_BORDERCOLOR_INT_OPAQUE_BLACK: + desc->BorderColor[0] = 0.0f; + desc->BorderColor[1] = 0.0f; + desc->BorderColor[2] = 0.0f; + desc->BorderColor[3] = 1.0f; + break; + + case REFRESH_BORDERCOLOR_FLOAT_OPAQUE_WHITE: + case REFRESH_BORDERCOLOR_INT_OPAQUE_WHITE: + desc->BorderColor[0] = 1.0f; + desc->BorderColor[1] = 1.0f; + desc->BorderColor[2] = 1.0f; + desc->BorderColor[3] = 1.0f; + break; + + case REFRESH_BORDERCOLOR_FLOAT_TRANSPARENT_BLACK: + case REFRESH_BORDERCOLOR_INT_TRANSPARENT_BLACK: + desc->BorderColor[0] = 0.0f; + desc->BorderColor[1] = 0.0f; + desc->BorderColor[2] = 0.0f; + desc->BorderColor[3] = 0.0f; + break; + } +} + +static D3D11_FILTER RefreshToD3D11_Filter(Refresh_SamplerCreateInfo *createInfo) +{ + if (createInfo->minFilter == REFRESH_FILTER_LINEAR) + { + if (createInfo->magFilter == REFRESH_FILTER_LINEAR) + { + if (createInfo->mipmapMode == REFRESH_SAMPLERMIPMAPMODE_LINEAR) + { + return D3D11_FILTER_MIN_MAG_MIP_LINEAR; + } + else + { + return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + } + } + else + { + if (createInfo->mipmapMode == REFRESH_SAMPLERMIPMAPMODE_LINEAR) + { + return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + } + else + { + return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; + } + } + } + else + { + if (createInfo->magFilter == REFRESH_FILTER_LINEAR) + { + if (createInfo->mipmapMode == REFRESH_SAMPLERMIPMAPMODE_LINEAR) + { + return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; + } + else + { + return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + } + } + else + { + if (createInfo->mipmapMode == REFRESH_SAMPLERMIPMAPMODE_LINEAR) + { + return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; + } + else + { + return D3D11_FILTER_MIN_MAG_MIP_POINT; + } + } + } +} + +/* Structs */ + +typedef struct D3D11Texture D3D11Texture; + +typedef struct D3D11TextureSubresource +{ + D3D11Texture *parent; + Uint32 layer; + Uint32 level; + Uint32 index; + + ID3D11RenderTargetView *colorTargetView; /* NULL if not a color target */ + ID3D11DepthStencilView *depthStencilTargetView; /* NULL if not a depth stencil target */ + ID3D11ShaderResourceView *srv; /* NULL if not a storage texture */ + ID3D11UnorderedAccessView *uav; /* NULL if not a storage texture */ + ID3D11Resource *msaaHandle; /* NULL if not using MSAA */ + ID3D11RenderTargetView *msaaTargetView; /* NULL if not an MSAA color target */ + + SDL_atomic_t referenceCount; +} D3D11TextureSubresource; + +struct D3D11Texture +{ + /* D3D Handles */ + ID3D11Resource *handle; /* ID3D11Texture2D* or ID3D11Texture3D* */ + ID3D11ShaderResourceView *shaderView; + + D3D11TextureSubresource *subresources; + Uint32 subresourceCount; /* layerCount * levelCount */ + + /* Basic Info */ + Refresh_TextureFormat format; + Uint32 width; + Uint32 height; + Uint32 depth; + Uint32 levelCount; + Uint32 layerCount; + Uint8 isCube; + Uint8 isRenderTarget; +}; + +typedef struct D3D11TextureContainer +{ + Refresh_TextureCreateInfo createInfo; + D3D11Texture *activeTexture; + Uint8 canBeCycled; + + Uint32 textureCapacity; + Uint32 textureCount; + D3D11Texture **textures; + + char *debugName; +} D3D11TextureContainer; + +typedef struct D3D11Fence +{ + ID3D11Query *handle; + SDL_atomic_t referenceCount; +} D3D11Fence; + +typedef struct D3D11WindowData +{ + SDL_Window *window; + IDXGISwapChain *swapchain; + D3D11Texture texture; + D3D11TextureContainer textureContainer; + Refresh_PresentMode presentMode; + Refresh_SwapchainComposition swapchainComposition; + DXGI_FORMAT swapchainFormat; + DXGI_COLOR_SPACE_TYPE swapchainColorSpace; + D3D11Fence *inFlightFences[MAX_FRAMES_IN_FLIGHT]; + Uint32 frameCounter; +} D3D11WindowData; + +typedef struct D3D11Shader +{ + ID3D11DeviceChild *shader; /* ID3D11VertexShader, ID3D11PixelShader, ID3D11ComputeShader */ + void* bytecode; + size_t bytecodeLength; +} D3D11Shader; + +typedef struct D3D11GraphicsPipeline +{ + float blendConstants[4]; + Sint32 numColorAttachments; + DXGI_FORMAT colorAttachmentFormats[MAX_COLOR_TARGET_BINDINGS]; + ID3D11BlendState *colorAttachmentBlendState; + + Refresh_MultisampleState multisampleState; + + Uint8 hasDepthStencilAttachment; + DXGI_FORMAT depthStencilAttachmentFormat; + ID3D11DepthStencilState *depthStencilState; + Uint32 stencilRef; + + Refresh_PrimitiveType primitiveType; + ID3D11RasterizerState *rasterizerState; + + ID3D11VertexShader *vertexShader; + ID3D11PixelShader *fragmentShader; + + ID3D11InputLayout *inputLayout; + Uint32 *vertexStrides; + + Uint32 vertexSamplerCount; + Uint32 vertexStorageTextureCount; + Uint32 vertexStorageBufferCount; + Uint32 vertexUniformBufferCount; + + Uint32 fragmentSamplerCount; + Uint32 fragmentStorageTextureCount; + Uint32 fragmentStorageBufferCount; + Uint32 fragmentUniformBufferCount; +} D3D11GraphicsPipeline; + +typedef struct D3D11ComputePipeline +{ + ID3D11ComputeShader *computeShader; + + Uint32 readOnlyStorageTextureCount; + Uint32 readWriteStorageTextureCount; + Uint32 readOnlyStorageBufferCount; + Uint32 readWriteStorageBufferCount; + Uint32 uniformBufferCount; +} D3D11ComputePipeline; + +typedef struct D3D11Buffer +{ + ID3D11Buffer *handle; + ID3D11UnorderedAccessView *uav; + ID3D11ShaderResourceView *srv; + Uint32 size; + SDL_atomic_t referenceCount; +} D3D11Buffer; + +typedef struct D3D11BufferContainer +{ + D3D11Buffer *activeBuffer; + + Uint32 bufferCapacity; + Uint32 bufferCount; + D3D11Buffer **buffers; + + D3D11_BUFFER_DESC bufferDesc; + + char *debugName; +} D3D11BufferContainer; + +typedef struct D3D11BufferTransfer +{ + ID3D11Buffer *stagingBuffer; +} D3D11BufferTransfer; + +typedef struct D3D11TextureTransfer +{ + Uint8 *data; + + /* TODO: can get rid of all of this by using a compute shader for texture-to-buffer copy */ + ID3D11Resource *downloadTexture; + Uint32 downloadWidth; + Uint32 downloadHeight; + Uint32 downloadDepth; + Uint32 downloadBytesPerRow; + Uint32 downloadBytesPerDepthSlice; + SDL_bool downloadTightlyPacked; +} D3D11TextureTransfer; + +typedef struct D3D11TransferBuffer +{ + Uint32 size; + SDL_atomic_t referenceCount; + + union + { + D3D11BufferTransfer bufferTransfer; + D3D11TextureTransfer textureTransfer; + }; +} D3D11TransferBuffer; + +typedef struct D3D11TransferBufferContainer +{ + Refresh_TransferUsage usage; + Refresh_TransferBufferMapFlags mapFlags; + + D3D11TransferBuffer *activeBuffer; + + /* These are all the buffers that have been used by this container. + * If the resource is bound and then updated with DISCARD, a new resource + * will be added to this list. + * These can be reused after they are submitted and command processing is complete. + */ + Uint32 bufferCapacity; + Uint32 bufferCount; + D3D11TransferBuffer **buffers; +} D3D11TransferBufferContainer; + +typedef struct D3D11UniformBuffer +{ + D3D11BufferContainer *bufferContainer; + + Uint32 drawOffset; + Uint32 offset; + Uint32 currentBlockSize; +} D3D11UniformBuffer; + +typedef struct D3D11Renderer D3D11Renderer; + +typedef struct D3D11CommandBuffer +{ + CommandBufferCommonHeader common; + D3D11Renderer *renderer; + + /* Deferred Context */ + ID3D11DeviceContext1 *context; + + /* Presentation */ + D3D11WindowData **windowDatas; + Uint32 windowDataCount; + Uint32 windowDataCapacity; + + /* Render Pass */ + D3D11GraphicsPipeline *graphicsPipeline; + + /* Render Pass MSAA resolve */ + D3D11Texture *colorTargetResolveTexture[MAX_COLOR_TARGET_BINDINGS]; + Uint32 colorTargetResolveSubresourceIndex[MAX_COLOR_TARGET_BINDINGS]; + ID3D11Resource *colorTargetMsaaHandle[MAX_COLOR_TARGET_BINDINGS]; + + /* Compute Pass */ + D3D11ComputePipeline *computePipeline; + + /* Resource slot state */ + + SDL_bool needVertexSamplerBind; + SDL_bool needVertexResourceBind; + + SDL_bool needFragmentSamplerBind; + SDL_bool needFragmentResourceBind; + + SDL_bool needComputeUAVBind; + SDL_bool needComputeSRVBind; + + ID3D11SamplerState *vertexSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE]; + ID3D11ShaderResourceView *vertexShaderResourceViews[ + MAX_TEXTURE_SAMPLERS_PER_STAGE + + MAX_STORAGE_BUFFERS_PER_STAGE + + MAX_STORAGE_TEXTURES_PER_STAGE + ]; + + ID3D11SamplerState *fragmentSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE]; + ID3D11ShaderResourceView *fragmentShaderResourceViews[ + MAX_TEXTURE_SAMPLERS_PER_STAGE + + MAX_STORAGE_BUFFERS_PER_STAGE + + MAX_STORAGE_TEXTURES_PER_STAGE + ]; + + ID3D11ShaderResourceView *computeShaderResourceViews[ + MAX_STORAGE_TEXTURES_PER_STAGE + + MAX_STORAGE_BUFFERS_PER_STAGE + ]; + ID3D11UnorderedAccessView *computeUnorderedAccessViews[ + MAX_STORAGE_TEXTURES_PER_STAGE + + MAX_STORAGE_BUFFERS_PER_STAGE + ]; + + /* Uniform buffers */ + D3D11UniformBuffer *vertexUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; + D3D11UniformBuffer *fragmentUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; + D3D11UniformBuffer *computeUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; + + SDL_bool vertexUniformBufferNeedsReset[MAX_UNIFORM_BUFFERS_PER_STAGE]; + SDL_bool fragmentUniformBufferNeedsReset[MAX_UNIFORM_BUFFERS_PER_STAGE]; + SDL_bool computeUniformBufferNeedsReset[MAX_UNIFORM_BUFFERS_PER_STAGE]; + + Uint32 initializedVertexUniformBufferCount; + Uint32 initializedFragmentUniformBufferCount; + Uint32 initializedComputeUniformBufferCount; + + /* Fences */ + D3D11Fence *fence; + Uint8 autoReleaseFence; + + /* Reference Counting */ + D3D11Buffer **usedBuffers; + Uint32 usedBufferCount; + Uint32 usedBufferCapacity; + + D3D11TransferBuffer **usedTransferBuffers; + Uint32 usedTransferBufferCount; + Uint32 usedTransferBufferCapacity; + + D3D11TextureSubresource **usedTextureSubresources; + Uint32 usedTextureSubresourceCount; + Uint32 usedTextureSubresourceCapacity; +} D3D11CommandBuffer; + +typedef struct D3D11Sampler +{ + ID3D11SamplerState *handle; +} D3D11Sampler; + +typedef struct D3D11OcclusionQuery +{ + ID3D11Query *handle; +} D3D11OcclusionQuery; + +struct D3D11Renderer +{ + ID3D11Device1 *device; + ID3D11DeviceContext *immediateContext; + IDXGIFactory1 *factory; + IDXGIAdapter1 *adapter; + IDXGIDebug *dxgiDebug; +#ifdef HAVE_IDXGIINFOQUEUE + IDXGIInfoQueue *dxgiInfoQueue; +#endif + void *d3d11_dll; + void *dxgi_dll; + void *dxgidebug_dll; + + Uint8 debugMode; + BOOL supportsTearing; + Uint8 supportsFlipDiscard; + + ID3DUserDefinedAnnotation *annotation; + SDL_iconv_t iconv; + + /* Blit */ + Refresh_Shader *fullscreenVertexShader; + Refresh_Shader *blitFrom2DPixelShader; + Refresh_Shader *blitFrom2DArrayPixelShader; + Refresh_GraphicsPipeline *blitFrom2DPipeline; + Refresh_GraphicsPipeline *blitFrom2DArrayPipeline; /* also cube */ + Refresh_Sampler *blitNearestSampler; + Refresh_Sampler *blitLinearSampler; + + /* Resource Tracking */ + D3D11WindowData **claimedWindows; + Uint32 claimedWindowCount; + Uint32 claimedWindowCapacity; + + D3D11CommandBuffer **availableCommandBuffers; + Uint32 availableCommandBufferCount; + Uint32 availableCommandBufferCapacity; + + D3D11CommandBuffer **submittedCommandBuffers; + Uint32 submittedCommandBufferCount; + Uint32 submittedCommandBufferCapacity; + + D3D11Fence **availableFences; + Uint32 availableFenceCount; + Uint32 availableFenceCapacity; + + D3D11TransferBufferContainer **transferBufferContainersToDestroy; + Uint32 transferBufferContainersToDestroyCount; + Uint32 transferBufferContainersToDestroyCapacity; + + D3D11BufferContainer **bufferContainersToDestroy; + Uint32 bufferContainersToDestroyCount; + Uint32 bufferContainersToDestroyCapacity; + + D3D11TextureContainer **textureContainersToDestroy; + Uint32 textureContainersToDestroyCount; + Uint32 textureContainersToDestroyCapacity; + + SDL_mutex *contextLock; + SDL_mutex *acquireCommandBufferLock; + SDL_mutex *fenceLock; + SDL_mutex *windowLock; +}; + +/* Null arrays for resetting shader resource slots */ + +ID3D11RenderTargetView *nullRTVs[ + MAX_COLOR_TARGET_BINDINGS +]; + +ID3D11ShaderResourceView *nullSRVs[ + MAX_TEXTURE_SAMPLERS_PER_STAGE + + MAX_STORAGE_TEXTURES_PER_STAGE + + MAX_STORAGE_BUFFERS_PER_STAGE +]; + +ID3D11SamplerState *nullSamplers[ + MAX_TEXTURE_SAMPLERS_PER_STAGE +]; + +ID3D11UnorderedAccessView *nullUAVs[ + MAX_STORAGE_TEXTURES_PER_STAGE + + MAX_STORAGE_BUFFERS_PER_STAGE +]; + +/* Logging */ + +static void D3D11_INTERNAL_LogError( + ID3D11Device1 *device, + const char *msg, + HRESULT res +) { + #define MAX_ERROR_LEN 1024 /* FIXME: Arbitrary! */ + + /* Buffer for text, ensure space for \0 terminator after buffer */ + char wszMsgBuff[MAX_ERROR_LEN + 1]; + DWORD dwChars; /* Number of chars returned. */ + + if (res == DXGI_ERROR_DEVICE_REMOVED) + { + res = ID3D11Device_GetDeviceRemovedReason(device); + } + + /* Try to get the message from the system errors. */ +#ifdef _WIN32 + dwChars = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + res, + 0, + wszMsgBuff, + MAX_ERROR_LEN, + NULL + ); +#else + /* FIXME: Do we have error strings in dxvk-native? -flibit */ + dwChars = 0; +#endif + + /* No message? Screw it, just post the code. */ + if (dwChars == 0) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s! Error Code: " HRESULT_FMT, msg, res); + return; + } + + /* Ensure valid range */ + dwChars = SDL_min(dwChars, MAX_ERROR_LEN); + + /* Trim whitespace from tail of message */ + while (dwChars > 0) + { + if (wszMsgBuff[dwChars - 1] <= ' ') + { + dwChars--; + } + else + { + break; + } + } + + /* Ensure null-terminated string */ + wszMsgBuff[dwChars] = '\0'; + + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s! Error Code: %s " HRESULT_FMT, msg, wszMsgBuff, res); +} + +/* Helper Functions */ + +static inline Uint32 D3D11_INTERNAL_CalcSubresource( + Uint32 mipLevel, + Uint32 arraySlice, + Uint32 numLevels +) { + return mipLevel + (arraySlice * numLevels); +} + +static inline Uint32 D3D11_INTERNAL_NextHighestAlignment( + Uint32 n, + Uint32 align +) { + return align * ((n + align - 1) / align); +} + +static DXGI_FORMAT D3D11_INTERNAL_GetTypelessFormat( + DXGI_FORMAT typedFormat +) { + switch (typedFormat) + { + case DXGI_FORMAT_D16_UNORM: + return DXGI_FORMAT_R16_TYPELESS; + case DXGI_FORMAT_D32_FLOAT: + return DXGI_FORMAT_R32_TYPELESS; + case DXGI_FORMAT_D24_UNORM_S8_UINT: + return DXGI_FORMAT_R24G8_TYPELESS; + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + return DXGI_FORMAT_R32G8X24_TYPELESS; + default: + return DXGI_FORMAT_UNKNOWN; + } +} + +static DXGI_FORMAT D3D11_INTERNAL_GetSampleableFormat( + DXGI_FORMAT format +) { + switch (format) + { + case DXGI_FORMAT_R16_TYPELESS: + return DXGI_FORMAT_R16_UNORM; + case DXGI_FORMAT_R32_TYPELESS: + return DXGI_FORMAT_R32_FLOAT; + case DXGI_FORMAT_R24G8_TYPELESS: + return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + case DXGI_FORMAT_R32G8X24_TYPELESS: + return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; + default: + return format; + } +} + +/* Quit */ + +static void D3D11_INTERNAL_DestroyBufferContainer( + D3D11BufferContainer *container +) { + for (Uint32 i = 0; i < container->bufferCount; i += 1) + { + D3D11Buffer *d3d11Buffer = container->buffers[i]; + + if (d3d11Buffer->uav != NULL) + { + ID3D11UnorderedAccessView_Release(d3d11Buffer->uav); + } + + if (d3d11Buffer->srv != NULL) + { + ID3D11ShaderResourceView_Release(d3d11Buffer->srv); + } + + ID3D11Buffer_Release(d3d11Buffer->handle); + + SDL_free(d3d11Buffer); + } + + SDL_free(container->buffers); + SDL_free(container); +} + +static void D3D11_DestroyDevice( + Refresh_Device *device +) { + D3D11Renderer *renderer = (D3D11Renderer*) device->driverData; + + /* Flush any remaining GPU work... */ + D3D11_Wait(device->driverData); + + /* Release the window data */ + for (Sint32 i = renderer->claimedWindowCount - 1; i >= 0; i -= 1) + { + D3D11_UnclaimWindow(device->driverData, renderer->claimedWindows[i]->window); + } + SDL_free(renderer->claimedWindows); + + /* Release the blit resources */ + D3D11_INTERNAL_DestroyBlitPipelines(device->driverData); + + /* Release command buffer infrastructure */ + for (Uint32 i = 0; i < renderer->availableCommandBufferCount; i += 1) + { + D3D11CommandBuffer *commandBuffer = renderer->availableCommandBuffers[i]; + ID3D11DeviceContext_Release(commandBuffer->context); + SDL_free(commandBuffer->usedBuffers); + SDL_free(commandBuffer->usedTransferBuffers); + + for (Uint32 j = 0; j < commandBuffer->initializedVertexUniformBufferCount; j += 1) + { + D3D11_INTERNAL_DestroyBufferContainer( + commandBuffer->vertexUniformBuffers[j]->bufferContainer + ); + SDL_free(commandBuffer->vertexUniformBuffers[j]); + } + + for (Uint32 j = 0; j < commandBuffer->initializedFragmentUniformBufferCount; j += 1) + { + D3D11_INTERNAL_DestroyBufferContainer( + commandBuffer->fragmentUniformBuffers[j]->bufferContainer + ); + SDL_free(commandBuffer->fragmentUniformBuffers[j]); + } + + for (Uint32 j = 0; j < commandBuffer->initializedComputeUniformBufferCount; j += 1) + { + D3D11_INTERNAL_DestroyBufferContainer( + commandBuffer->computeUniformBuffers[j]->bufferContainer + ); + SDL_free(commandBuffer->computeUniformBuffers[j]); + } + + SDL_free(commandBuffer); + } + SDL_free(renderer->availableCommandBuffers); + SDL_free(renderer->submittedCommandBuffers); + + /* Release fence infrastructure */ + for (Uint32 i = 0; i < renderer->availableFenceCount; i += 1) + { + D3D11Fence *fence = renderer->availableFences[i]; + ID3D11Query_Release(fence->handle); + SDL_free(fence); + } + SDL_free(renderer->availableFences); + + /* Release the annotation/iconv, if applicable */ + if (renderer->annotation != NULL) + { + ID3DUserDefinedAnnotation_Release(renderer->annotation); + } + if (renderer->iconv != NULL) + { + SDL_iconv_close(renderer->iconv); + } + + /* Release the mutexes */ + SDL_DestroyMutex(renderer->acquireCommandBufferLock); + SDL_DestroyMutex(renderer->contextLock); + SDL_DestroyMutex(renderer->fenceLock); + SDL_DestroyMutex(renderer->windowLock); + + /* Release the device and associated objects */ + ID3D11DeviceContext_Release(renderer->immediateContext); + ID3D11Device_Release(renderer->device); + IDXGIAdapter_Release(renderer->adapter); + IDXGIFactory_Release(renderer->factory); + + /* Report leaks and clean up debug objects */ + if (renderer->dxgiDebug) + { + IDXGIDebug_ReportLiveObjects( + renderer->dxgiDebug, + D3D_IID_DXGI_DEBUG_ALL, + DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_DETAIL + ); + IDXGIDebug_Release(renderer->dxgiDebug); + } + +#ifdef HAVE_IDXGIINFOQUEUE + if (renderer->dxgiInfoQueue) + { + IDXGIInfoQueue_Release(renderer->dxgiInfoQueue); + } +#endif + + /* Release the DLLs */ + SDL_UnloadObject(renderer->d3d11_dll); + SDL_UnloadObject(renderer->dxgi_dll); + if (renderer->dxgidebug_dll) + { + SDL_UnloadObject(renderer->dxgidebug_dll); + } + + /* Free the primary structures */ + SDL_free(renderer); + SDL_free(device); +} + +/* Resource tracking */ + +static void D3D11_INTERNAL_TrackBuffer( + D3D11CommandBuffer *commandBuffer, + D3D11Buffer *buffer +) { + TRACK_RESOURCE( + buffer, + D3D11Buffer*, + usedBuffers, + usedBufferCount, + usedBufferCapacity + ); +} + +static void D3D11_INTERNAL_TrackTransferBuffer( + D3D11CommandBuffer *commandBuffer, + D3D11TransferBuffer *buffer +) { + TRACK_RESOURCE( + buffer, + D3D11TransferBuffer*, + usedTransferBuffers, + usedTransferBufferCount, + usedTransferBufferCapacity + ); +} + +static void D3D11_INTERNAL_TrackTextureSubresource( + D3D11CommandBuffer *commandBuffer, + D3D11TextureSubresource *textureSubresource +) { + TRACK_RESOURCE( + textureSubresource, + D3D11TextureSubresource*, + usedTextureSubresources, + usedTextureSubresourceCount, + usedTextureSubresourceCapacity + ); +} + +/* Disposal */ + +static void D3D11_INTERNAL_DestroyTextureContainer( + D3D11TextureContainer *container +) { + for (Uint32 i = 0; i < container->textureCount; i += 1) + { + D3D11Texture *d3d11Texture = container->textures[i]; + + if (d3d11Texture->shaderView) + { + ID3D11ShaderResourceView_Release(d3d11Texture->shaderView); + } + + for (Uint32 subresourceIndex = 0; subresourceIndex < d3d11Texture->subresourceCount; subresourceIndex += 1) + { + if (d3d11Texture->subresources[subresourceIndex].msaaHandle != NULL) + { + ID3D11Resource_Release(d3d11Texture->subresources[subresourceIndex].msaaHandle); + } + + if (d3d11Texture->subresources[subresourceIndex].msaaTargetView != NULL) + { + ID3D11RenderTargetView_Release(d3d11Texture->subresources[subresourceIndex].msaaTargetView); + } + + if (d3d11Texture->subresources[subresourceIndex].colorTargetView != NULL) + { + ID3D11RenderTargetView_Release(d3d11Texture->subresources[subresourceIndex].colorTargetView); + } + + if (d3d11Texture->subresources[subresourceIndex].depthStencilTargetView != NULL) + { + ID3D11DepthStencilView_Release(d3d11Texture->subresources[subresourceIndex].depthStencilTargetView); + } + + if (d3d11Texture->subresources[subresourceIndex].srv != NULL) + { + ID3D11ShaderResourceView_Release(d3d11Texture->subresources[subresourceIndex].srv); + } + + if (d3d11Texture->subresources[subresourceIndex].uav != NULL) + { + ID3D11UnorderedAccessView_Release(d3d11Texture->subresources[subresourceIndex].uav); + } + } + SDL_free(d3d11Texture->subresources); + + ID3D11Resource_Release(d3d11Texture->handle); + } + + SDL_free(container->textures); + SDL_free(container); +} + +static void D3D11_ReleaseTexture( + Refresh_Renderer *driverData, + Refresh_Texture *texture +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TextureContainer *container = (D3D11TextureContainer*) texture; + + SDL_LockMutex(renderer->contextLock); + + EXPAND_ARRAY_IF_NEEDED( + renderer->textureContainersToDestroy, + D3D11TextureContainer*, + renderer->textureContainersToDestroyCount + 1, + renderer->textureContainersToDestroyCapacity, + renderer->textureContainersToDestroyCapacity + 1 + ); + + renderer->textureContainersToDestroy[ + renderer->textureContainersToDestroyCount + ] = container; + renderer->textureContainersToDestroyCount += 1; + + SDL_UnlockMutex(renderer->contextLock); +} + +static void D3D11_ReleaseSampler( + Refresh_Renderer *driverData, + Refresh_Sampler *sampler +) { + (void) driverData; /* used by other backends */ + D3D11Sampler *d3d11Sampler = (D3D11Sampler*) sampler; + ID3D11SamplerState_Release(d3d11Sampler->handle); + SDL_free(d3d11Sampler); +} + +static void D3D11_ReleaseBuffer( + Refresh_Renderer *driverData, + Refresh_Buffer *buffer +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11BufferContainer *container = (D3D11BufferContainer*) buffer; + + SDL_LockMutex(renderer->contextLock); + + EXPAND_ARRAY_IF_NEEDED( + renderer->bufferContainersToDestroy, + D3D11BufferContainer*, + renderer->bufferContainersToDestroyCount + 1, + renderer->bufferContainersToDestroyCapacity, + renderer->bufferContainersToDestroyCapacity + 1 + ); + + renderer->bufferContainersToDestroy[ + renderer->bufferContainersToDestroyCount + ] = container; + renderer->bufferContainersToDestroyCount += 1; + + SDL_UnlockMutex(renderer->contextLock); +} + +static void D3D11_ReleaseTransferBuffer( + Refresh_Renderer *driverData, + Refresh_TransferBuffer *transferBuffer +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + + SDL_LockMutex(renderer->contextLock); + + EXPAND_ARRAY_IF_NEEDED( + renderer->transferBufferContainersToDestroy, + D3D11TransferBufferContainer*, + renderer->transferBufferContainersToDestroyCount + 1, + renderer->transferBufferContainersToDestroyCapacity, + renderer->transferBufferContainersToDestroyCapacity + 1 + ); + + renderer->transferBufferContainersToDestroy[ + renderer->transferBufferContainersToDestroyCount + ] = (D3D11TransferBufferContainer*) transferBuffer; + renderer->transferBufferContainersToDestroyCount += 1; + + SDL_UnlockMutex(renderer->contextLock); +} + +static void D3D11_INTERNAL_DestroyTransferBufferContainer( + D3D11TransferBufferContainer *transferBufferContainer +) { + for (Uint32 i = 0; i < transferBufferContainer->bufferCount; i += 1) + { + if (transferBufferContainer->usage == REFRESH_TRANSFERUSAGE_BUFFER) + { + ID3D11Buffer_Release(transferBufferContainer->buffers[i]->bufferTransfer.stagingBuffer); + } + else /* TEXTURE */ + { + SDL_free(transferBufferContainer->buffers[i]->textureTransfer.data); + if (transferBufferContainer->buffers[i]->textureTransfer.downloadTexture != NULL) + { + ID3D11Resource_Release(transferBufferContainer->buffers[i]->textureTransfer.downloadTexture); + } + } + SDL_free(transferBufferContainer->buffers[i]); + } + SDL_free(transferBufferContainer->buffers); +} + +static void D3D11_ReleaseShader( + Refresh_Renderer *driverData, + Refresh_Shader *shader +) { + (void) driverData; /* used by other backends */ + D3D11Shader *d3dShader = (D3D11Shader*) shader; + + if (d3dShader->shader) + { + ID3D11DeviceChild_Release(d3dShader->shader); + } + + if (d3dShader->bytecode) + { + SDL_free(d3dShader->bytecode); + } + + SDL_free(d3dShader); +} + +static void D3D11_ReleaseComputePipeline( + Refresh_Renderer *driverData, + Refresh_ComputePipeline *computePipeline +) { + D3D11ComputePipeline *d3d11ComputePipeline = (D3D11ComputePipeline*) computePipeline; + + ID3D11ComputeShader_Release(d3d11ComputePipeline->computeShader); + + /* TODO: teardown resource layout structure */ + + SDL_free(d3d11ComputePipeline); +} + +static void D3D11_ReleaseGraphicsPipeline( + Refresh_Renderer *driverData, + Refresh_GraphicsPipeline *graphicsPipeline +) { + (void) driverData; /* used by other backends */ + D3D11GraphicsPipeline *d3d11GraphicsPipeline = (D3D11GraphicsPipeline*) graphicsPipeline; + + ID3D11BlendState_Release(d3d11GraphicsPipeline->colorAttachmentBlendState); + ID3D11DepthStencilState_Release(d3d11GraphicsPipeline->depthStencilState); + ID3D11RasterizerState_Release(d3d11GraphicsPipeline->rasterizerState); + + if (d3d11GraphicsPipeline->inputLayout) + { + ID3D11InputLayout_Release(d3d11GraphicsPipeline->inputLayout); + } + if (d3d11GraphicsPipeline->vertexStrides) + { + SDL_free(d3d11GraphicsPipeline->vertexStrides); + } + + ID3D11VertexShader_Release(d3d11GraphicsPipeline->vertexShader); + ID3D11PixelShader_Release(d3d11GraphicsPipeline->fragmentShader); + + /* TODO: teardown resource layout structure */ + + SDL_free(d3d11GraphicsPipeline); +} + +static void D3D11_ReleaseOcclusionQuery( + Refresh_Renderer *renderer, + Refresh_OcclusionQuery *query +) { + D3D11OcclusionQuery *d3dQuery = (D3D11OcclusionQuery*) query; + ID3D11Query_Release(d3dQuery->handle); + SDL_free(query); +} + +/* State Creation */ + +static ID3D11BlendState* D3D11_INTERNAL_FetchBlendState( + D3D11Renderer *renderer, + Uint32 numColorAttachments, + Refresh_ColorAttachmentDescription *colorAttachments +) { + ID3D11BlendState *result; + D3D11_BLEND_DESC blendDesc; + HRESULT res; + + /* Create a new blend state. + * The spec says the driver will not create duplicate states, so there's no need to cache. + */ + SDL_zero(blendDesc); /* needed for any unused RT entries */ + + blendDesc.AlphaToCoverageEnable = FALSE; + blendDesc.IndependentBlendEnable = TRUE; + + for (Uint32 i = 0; i < numColorAttachments; i += 1) + { + blendDesc.RenderTarget[i].BlendEnable = colorAttachments[i].blendState.blendEnable; + blendDesc.RenderTarget[i].BlendOp = RefreshToD3D11_BlendOp[ + colorAttachments[i].blendState.colorBlendOp + ]; + blendDesc.RenderTarget[i].BlendOpAlpha = RefreshToD3D11_BlendOp[ + colorAttachments[i].blendState.alphaBlendOp + ]; + blendDesc.RenderTarget[i].DestBlend = RefreshToD3D11_BlendFactor[ + colorAttachments[i].blendState.dstColorBlendFactor + ]; + blendDesc.RenderTarget[i].DestBlendAlpha = RefreshToD3D11_BlendFactorAlpha[ + colorAttachments[i].blendState.dstAlphaBlendFactor + ]; + blendDesc.RenderTarget[i].RenderTargetWriteMask = colorAttachments[i].blendState.colorWriteMask; + blendDesc.RenderTarget[i].SrcBlend = RefreshToD3D11_BlendFactor[ + colorAttachments[i].blendState.srcColorBlendFactor + ]; + blendDesc.RenderTarget[i].SrcBlendAlpha = RefreshToD3D11_BlendFactorAlpha[ + colorAttachments[i].blendState.srcAlphaBlendFactor + ]; + } + + res = ID3D11Device_CreateBlendState( + renderer->device, + &blendDesc, + &result + ); + ERROR_CHECK_RETURN("Could not create blend state", NULL); + + return result; +} + +static ID3D11DepthStencilState* D3D11_INTERNAL_FetchDepthStencilState( + D3D11Renderer *renderer, + Refresh_DepthStencilState depthStencilState +) { + ID3D11DepthStencilState *result; + D3D11_DEPTH_STENCIL_DESC dsDesc; + HRESULT res; + + /* Create a new depth-stencil state. + * The spec says the driver will not create duplicate states, so there's no need to cache. + */ + dsDesc.DepthEnable = depthStencilState.depthTestEnable; + dsDesc.StencilEnable = depthStencilState.stencilTestEnable; + dsDesc.DepthFunc = RefreshToD3D11_CompareOp[depthStencilState.compareOp]; + dsDesc.DepthWriteMask = ( + depthStencilState.depthWriteEnable ? + D3D11_DEPTH_WRITE_MASK_ALL : + D3D11_DEPTH_WRITE_MASK_ZERO + ); + + dsDesc.BackFace.StencilFunc = RefreshToD3D11_CompareOp[depthStencilState.backStencilState.compareOp]; + dsDesc.BackFace.StencilDepthFailOp = RefreshToD3D11_StencilOp[depthStencilState.backStencilState.depthFailOp]; + dsDesc.BackFace.StencilFailOp = RefreshToD3D11_StencilOp[depthStencilState.backStencilState.failOp]; + dsDesc.BackFace.StencilPassOp = RefreshToD3D11_StencilOp[depthStencilState.backStencilState.passOp]; + + dsDesc.FrontFace.StencilFunc = RefreshToD3D11_CompareOp[depthStencilState.frontStencilState.compareOp]; + dsDesc.FrontFace.StencilDepthFailOp = RefreshToD3D11_StencilOp[depthStencilState.frontStencilState.depthFailOp]; + dsDesc.FrontFace.StencilFailOp = RefreshToD3D11_StencilOp[depthStencilState.frontStencilState.failOp]; + dsDesc.FrontFace.StencilPassOp = RefreshToD3D11_StencilOp[depthStencilState.frontStencilState.passOp]; + + dsDesc.StencilReadMask = depthStencilState.compareMask; + dsDesc.StencilWriteMask = depthStencilState.writeMask; + + if (depthStencilState.depthBoundsTestEnable) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11 does not support Depth Bounds tests!"); + } + + res = ID3D11Device_CreateDepthStencilState( + renderer->device, + &dsDesc, + &result + ); + ERROR_CHECK_RETURN("Could not create depth-stencil state", NULL); + + return result; +} + +static ID3D11RasterizerState* D3D11_INTERNAL_FetchRasterizerState( + D3D11Renderer *renderer, + Refresh_RasterizerState rasterizerState +) { + ID3D11RasterizerState *result; + D3D11_RASTERIZER_DESC rasterizerDesc; + HRESULT res; + + /* Create a new rasterizer state. + * The spec says the driver will not create duplicate states, so there's no need to cache. + */ + rasterizerDesc.AntialiasedLineEnable = FALSE; + rasterizerDesc.CullMode = RefreshToD3D11_CullMode[rasterizerState.cullMode]; + rasterizerDesc.DepthBias = SDL_lroundf(rasterizerState.depthBiasConstantFactor); + rasterizerDesc.DepthBiasClamp = rasterizerState.depthBiasClamp; + rasterizerDesc.DepthClipEnable = TRUE; + rasterizerDesc.FillMode = (rasterizerState.fillMode == REFRESH_FILLMODE_FILL) ? D3D11_FILL_SOLID : D3D11_FILL_WIREFRAME; + rasterizerDesc.FrontCounterClockwise = (rasterizerState.frontFace == REFRESH_FRONTFACE_COUNTER_CLOCKWISE); + rasterizerDesc.MultisampleEnable = TRUE; /* only applies to MSAA render targets */ + rasterizerDesc.ScissorEnable = TRUE; + rasterizerDesc.SlopeScaledDepthBias = rasterizerState.depthBiasSlopeFactor; + + res = ID3D11Device_CreateRasterizerState( + renderer->device, + &rasterizerDesc, + &result + ); + ERROR_CHECK_RETURN("Could not create rasterizer state", NULL); + + return result; +} + +static Uint32 D3D11_INTERNAL_FindIndexOfVertexBinding( + Uint32 targetBinding, + const Refresh_VertexBinding *bindings, + Uint32 numBindings +) { + for (Uint32 i = 0; i < numBindings; i += 1) + { + if (bindings[i].binding == targetBinding) + { + return i; + } + } + + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not find vertex binding %u!", targetBinding); + return 0; +} + +static ID3D11InputLayout* D3D11_INTERNAL_FetchInputLayout( + D3D11Renderer *renderer, + Refresh_VertexInputState inputState, + void *shaderBytes, + size_t shaderByteLength +) { + ID3D11InputLayout *result = NULL; + D3D11_INPUT_ELEMENT_DESC *elementDescs; + Uint32 bindingIndex; + HRESULT res; + + /* Don't bother creating/fetching an input layout if there are no attributes. */ + if (inputState.vertexAttributeCount == 0) + { + return NULL; + } + + /* Allocate an array of vertex elements */ + elementDescs = SDL_stack_alloc( + D3D11_INPUT_ELEMENT_DESC, + inputState.vertexAttributeCount + ); + + /* Create the array of input elements */ + for (Uint32 i = 0; i < inputState.vertexAttributeCount; i += 1) + { + elementDescs[i].AlignedByteOffset = inputState.vertexAttributes[i].offset; + elementDescs[i].Format = RefreshToD3D11_VertexFormat[ + inputState.vertexAttributes[i].format + ]; + elementDescs[i].InputSlot = inputState.vertexAttributes[i].binding; + + bindingIndex = D3D11_INTERNAL_FindIndexOfVertexBinding( + elementDescs[i].InputSlot, + inputState.vertexBindings, + inputState.vertexBindingCount + ); + elementDescs[i].InputSlotClass = RefreshToD3D11_VertexInputRate[ + inputState.vertexBindings[bindingIndex].inputRate + ]; + /* The spec requires this to be 0 for per-vertex data */ + elementDescs[i].InstanceDataStepRate = ( + inputState.vertexBindings[bindingIndex].stepRate > 0 ? + inputState.vertexBindings[bindingIndex].stepRate : + 0 + ); + + elementDescs[i].SemanticIndex = inputState.vertexAttributes[i].location; + elementDescs[i].SemanticName = "TEXCOORD"; + } + + res = ID3D11Device_CreateInputLayout( + renderer->device, + elementDescs, + inputState.vertexAttributeCount, + shaderBytes, + shaderByteLength, + &result + ); + if (FAILED(res)) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not create input layout! Error: " HRESULT_FMT, res); + SDL_stack_free(elementDescs); + return NULL; + } + + /* FIXME: + * These are not cached by the driver! Should we cache them, or allow duplicates? + * If we have one input layout per graphics pipeline maybe that wouldn't be so bad...? + */ + + SDL_stack_free(elementDescs); + return result; +} + +/* Pipeline Creation */ + +static Refresh_ComputePipeline* D3D11_CreateComputePipeline( + Refresh_Renderer *driverData, + Refresh_ComputePipelineCreateInfo *pipelineCreateInfo +) { + (void) driverData; /* used by other backends */ + D3D11Shader *shader = (D3D11Shader*) pipelineCreateInfo->computeShader; + D3D11ComputePipeline *pipeline = SDL_malloc(sizeof(D3D11ComputePipeline)); + + pipeline->computeShader = (ID3D11ComputeShader*) shader->shader; + ID3D11ComputeShader_AddRef(pipeline->computeShader); + + pipeline->readOnlyStorageTextureCount = pipelineCreateInfo->pipelineResourceInfo.readOnlyStorageTextureCount; + pipeline->readWriteStorageTextureCount = pipelineCreateInfo->pipelineResourceInfo.readWriteStorageTextureCount; + pipeline->readOnlyStorageBufferCount = pipelineCreateInfo->pipelineResourceInfo.readOnlyStorageBufferCount; + pipeline->readWriteStorageBufferCount = pipelineCreateInfo->pipelineResourceInfo.readWriteStorageBufferCount; + pipeline->uniformBufferCount = pipelineCreateInfo->pipelineResourceInfo.uniformBufferCount; + + return (Refresh_ComputePipeline*) pipeline; +} + +static Refresh_GraphicsPipeline* D3D11_CreateGraphicsPipeline( + Refresh_Renderer *driverData, + Refresh_GraphicsPipelineCreateInfo *pipelineCreateInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11Shader *vertShader = (D3D11Shader*) pipelineCreateInfo->vertexShader; + D3D11Shader *fragShader = (D3D11Shader*) pipelineCreateInfo->fragmentShader; + D3D11GraphicsPipeline *pipeline = SDL_malloc(sizeof(D3D11GraphicsPipeline)); + Uint32 i; + + /* Blend */ + + pipeline->colorAttachmentBlendState = D3D11_INTERNAL_FetchBlendState( + renderer, + pipelineCreateInfo->attachmentInfo.colorAttachmentCount, + pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions + ); + + pipeline->numColorAttachments = pipelineCreateInfo->attachmentInfo.colorAttachmentCount; + for (i = 0; i < pipeline->numColorAttachments; i += 1) + { + pipeline->colorAttachmentFormats[i] = RefreshToD3D11_TextureFormat[ + pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions[i].format + ]; + } + + pipeline->blendConstants[0] = pipelineCreateInfo->blendConstants[0]; + pipeline->blendConstants[1] = pipelineCreateInfo->blendConstants[1]; + pipeline->blendConstants[2] = pipelineCreateInfo->blendConstants[2]; + pipeline->blendConstants[3] = pipelineCreateInfo->blendConstants[3]; + + /* Multisample */ + + pipeline->multisampleState = pipelineCreateInfo->multisampleState; + + /* Depth-Stencil */ + + pipeline->depthStencilState = D3D11_INTERNAL_FetchDepthStencilState( + renderer, + pipelineCreateInfo->depthStencilState + ); + + pipeline->hasDepthStencilAttachment = pipelineCreateInfo->attachmentInfo.hasDepthStencilAttachment; + pipeline->depthStencilAttachmentFormat = RefreshToD3D11_TextureFormat[ + pipelineCreateInfo->attachmentInfo.depthStencilFormat + ]; + pipeline->stencilRef = pipelineCreateInfo->depthStencilState.reference; + + /* Rasterizer */ + + pipeline->primitiveType = pipelineCreateInfo->primitiveType; + pipeline->rasterizerState = D3D11_INTERNAL_FetchRasterizerState( + renderer, + pipelineCreateInfo->rasterizerState + ); + + /* Shaders */ + + pipeline->vertexShader = (ID3D11VertexShader*) vertShader->shader; + ID3D11VertexShader_AddRef(pipeline->vertexShader); + + pipeline->fragmentShader = (ID3D11PixelShader*) fragShader->shader; + ID3D11PixelShader_AddRef(pipeline->fragmentShader); + + /* Input Layout */ + + pipeline->inputLayout = D3D11_INTERNAL_FetchInputLayout( + renderer, + pipelineCreateInfo->vertexInputState, + vertShader->bytecode, + vertShader->bytecodeLength + ); + + if (pipelineCreateInfo->vertexInputState.vertexBindingCount > 0) + { + pipeline->vertexStrides = SDL_malloc( + sizeof(Uint32) * + pipelineCreateInfo->vertexInputState.vertexBindingCount + ); + + for (i = 0; i < pipelineCreateInfo->vertexInputState.vertexBindingCount; i += 1) + { + pipeline->vertexStrides[i] = pipelineCreateInfo->vertexInputState.vertexBindings[i].stride; + } + } + else + { + pipeline->vertexStrides = NULL; + } + + /* Resource layout */ + + pipeline->vertexSamplerCount = pipelineCreateInfo->vertexResourceInfo.samplerCount; + pipeline->vertexStorageTextureCount = pipelineCreateInfo->vertexResourceInfo.storageTextureCount; + pipeline->vertexStorageBufferCount = pipelineCreateInfo->vertexResourceInfo.storageBufferCount; + pipeline->vertexUniformBufferCount = pipelineCreateInfo->vertexResourceInfo.uniformBufferCount; + + pipeline->fragmentSamplerCount = pipelineCreateInfo->fragmentResourceInfo.samplerCount; + pipeline->fragmentStorageTextureCount = pipelineCreateInfo->fragmentResourceInfo.storageTextureCount; + pipeline->fragmentStorageBufferCount = pipelineCreateInfo->fragmentResourceInfo.storageBufferCount; + pipeline->fragmentUniformBufferCount = pipelineCreateInfo->fragmentResourceInfo.uniformBufferCount; + + return (Refresh_GraphicsPipeline*) pipeline; +} + +/* Debug Naming */ + +static void D3D11_INTERNAL_SetBufferName( + D3D11Renderer *renderer, + D3D11Buffer *buffer, + const char *text +) { + if (renderer->debugMode) + { + ID3D11DeviceChild_SetPrivateData( + buffer->handle, + &D3D_IID_D3DDebugObjectName, + (UINT) SDL_strlen(text), + text + ); + } +} + +static void D3D11_SetBufferName( + Refresh_Renderer *driverData, + Refresh_Buffer *buffer, + const char *text +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11BufferContainer *container = (D3D11BufferContainer*) buffer; + + if (renderer->debugMode) + { + container->debugName = SDL_realloc( + container->debugName, + SDL_strlen(text) + 1 + ); + + SDL_utf8strlcpy( + container->debugName, + text, + SDL_strlen(text) + 1 + ); + + for (Uint32 i = 0; i < container->bufferCount; i += 1) + { + D3D11_INTERNAL_SetBufferName( + renderer, + container->buffers[i], + text + ); + } + } +} + +static void D3D11_INTERNAL_SetTextureName( + D3D11Renderer *renderer, + D3D11Texture *texture, + const char *text +) { + if (renderer->debugMode) + { + ID3D11DeviceChild_SetPrivateData( + texture->handle, + &D3D_IID_D3DDebugObjectName, + (UINT) SDL_strlen(text), + text + ); + } +} + +static void D3D11_SetTextureName( + Refresh_Renderer *driverData, + Refresh_Texture *texture, + const char *text +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TextureContainer *container = (D3D11TextureContainer*) texture; + + if (renderer->debugMode) + { + container->debugName = SDL_realloc( + container->debugName, + SDL_strlen(text) + 1 + ); + + SDL_utf8strlcpy( + container->debugName, + text, + SDL_strlen(text) + 1 + ); + + for (Uint32 i = 0; i < container->textureCount; i += 1) + { + D3D11_INTERNAL_SetTextureName( + renderer, + container->textures[i], + text + ); + } + } +} + +static void D3D11_SetStringMarker( + Refresh_CommandBuffer *commandBuffer, + const char *text +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer; + + if (renderer->annotation == NULL) + { + return; + } + + wchar_t wstr[256]; + wchar_t *out = wstr; + size_t inlen, outlen, result; + + if (renderer->iconv == NULL) + { + renderer->iconv = SDL_iconv_open("WCHAR_T", "UTF-8"); + SDL_assert(renderer->iconv); + } + + /* Convert... */ + inlen = SDL_strlen(text) + 1; + outlen = sizeof(wstr); + result = SDL_iconv( + renderer->iconv, + &text, + &inlen, + (char**) &out, + &outlen + ); + + /* Check... */ + switch (result) + { + case SDL_ICONV_ERROR: + case SDL_ICONV_E2BIG: + case SDL_ICONV_EILSEQ: + case SDL_ICONV_EINVAL: + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to convert string marker to wchar_t!"); + return; + default: + break; + } + + /* Mark, finally. */ + ID3DUserDefinedAnnotation_SetMarker(renderer->annotation, wstr); +} + +/* Resource Creation */ + +static Refresh_Sampler* D3D11_CreateSampler( + Refresh_Renderer *driverData, + Refresh_SamplerCreateInfo *samplerCreateInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11_SAMPLER_DESC samplerDesc; + ID3D11SamplerState *samplerStateHandle; + D3D11Sampler *d3d11Sampler; + HRESULT res; + + samplerDesc.AddressU = RefreshToD3D11_SamplerAddressMode[samplerCreateInfo->addressModeU]; + samplerDesc.AddressV = RefreshToD3D11_SamplerAddressMode[samplerCreateInfo->addressModeV]; + samplerDesc.AddressW = RefreshToD3D11_SamplerAddressMode[samplerCreateInfo->addressModeW]; + + RefreshToD3D11_BorderColor( + samplerCreateInfo, + &samplerDesc + ); + + samplerDesc.ComparisonFunc = ( + samplerCreateInfo->compareEnable ? + RefreshToD3D11_CompareOp[samplerCreateInfo->compareOp] : + RefreshToD3D11_CompareOp[REFRESH_COMPAREOP_ALWAYS] + ); + samplerDesc.MaxAnisotropy = ( + samplerCreateInfo->anisotropyEnable ? + (UINT) samplerCreateInfo->maxAnisotropy : + 0 + ); + samplerDesc.Filter = RefreshToD3D11_Filter(samplerCreateInfo); + samplerDesc.MaxLOD = samplerCreateInfo->maxLod; + samplerDesc.MinLOD = samplerCreateInfo->minLod; + samplerDesc.MipLODBias = samplerCreateInfo->mipLodBias; + + res = ID3D11Device_CreateSamplerState( + renderer->device, + &samplerDesc, + &samplerStateHandle + ); + ERROR_CHECK_RETURN("Could not create sampler state", NULL); + + d3d11Sampler = (D3D11Sampler*) SDL_malloc(sizeof(D3D11Sampler)); + d3d11Sampler->handle = samplerStateHandle; + + return (Refresh_Sampler*) d3d11Sampler; +} + +Refresh_Shader* D3D11_CreateShader( + Refresh_Renderer *driverData, + Refresh_ShaderCreateInfo *shaderCreateInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11Shader* shaderModule; + Refresh_ShaderStage shaderStage = shaderCreateInfo->stage; + ID3D11DeviceChild *shader = NULL; + HRESULT res; + + if (shaderCreateInfo->format != REFRESH_SHADERFORMAT_DXBC) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Incompatible shader format for D3D11"); + return NULL; + } + + /* Create the shader from the byte blob */ + if (shaderStage == REFRESH_SHADERSTAGE_VERTEX) + { + res = ID3D11Device_CreateVertexShader( + renderer->device, + shaderCreateInfo->code, + shaderCreateInfo->codeSize, + NULL, + (ID3D11VertexShader**) &shader + ); + if (FAILED(res)) + { + D3D11_INTERNAL_LogError(renderer->device, "Could not create vertex shader", res); + return NULL; + } + } + else if (shaderStage == REFRESH_SHADERSTAGE_FRAGMENT) + { + res = ID3D11Device_CreatePixelShader( + renderer->device, + shaderCreateInfo->code, + shaderCreateInfo->codeSize, + NULL, + (ID3D11PixelShader**) &shader + ); + if (FAILED(res)) + { + D3D11_INTERNAL_LogError(renderer->device, "Could not create pixel shader", res); + return NULL; + } + } + else if (shaderStage == REFRESH_SHADERSTAGE_COMPUTE) + { + res = ID3D11Device_CreateComputeShader( + renderer->device, + shaderCreateInfo->code, + shaderCreateInfo->codeSize, + NULL, + (ID3D11ComputeShader**) &shader + ); + if (FAILED(res)) + { + D3D11_INTERNAL_LogError(renderer->device, "Could not create compute shader", res); + return NULL; + } + } + + /* Allocate and set up the shader module */ + shaderModule = (D3D11Shader*) SDL_calloc(1, sizeof(D3D11Shader)); + shaderModule->shader = shader; + if (shaderStage == REFRESH_SHADERSTAGE_VERTEX) + { + /* Store the raw bytecode and its length for creating InputLayouts */ + shaderModule->bytecode = SDL_malloc(shaderCreateInfo->codeSize); + SDL_memcpy(shaderModule->bytecode, shaderCreateInfo->code, shaderCreateInfo->codeSize); + shaderModule->bytecodeLength = shaderCreateInfo->codeSize; + } + + return (Refresh_Shader*) shaderModule; +} + +static D3D11Texture* D3D11_INTERNAL_CreateTexture( + D3D11Renderer *renderer, + Refresh_TextureCreateInfo *textureCreateInfo +) { + Uint8 isSampler, isColorTarget, isDepthStencil, isMultisample, needSubresourceSRV, needSubresourceUAV; + DXGI_FORMAT format; + ID3D11Resource *textureHandle; + ID3D11ShaderResourceView *srv = NULL; + D3D11Texture *d3d11Texture; + HRESULT res; + + isColorTarget = textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT; + isDepthStencil = textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT; + isSampler = textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_SAMPLER_BIT; + needSubresourceSRV = + (textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT) || + (textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT); + needSubresourceUAV = + (textureCreateInfo->usageFlags & REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT); + isMultisample = textureCreateInfo->sampleCount > 1; + + format = RefreshToD3D11_TextureFormat[textureCreateInfo->format]; + if (isDepthStencil) + { + format = D3D11_INTERNAL_GetTypelessFormat(format); + } + + if (textureCreateInfo->depth <= 1) + { + D3D11_TEXTURE2D_DESC desc2D; + + desc2D.BindFlags = 0; + if (isSampler || needSubresourceSRV) + { + desc2D.BindFlags |= D3D11_BIND_SHADER_RESOURCE; + } + if (needSubresourceUAV) + { + desc2D.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; + } + if (isColorTarget) + { + desc2D.BindFlags |= D3D11_BIND_RENDER_TARGET; + } + if (isDepthStencil) + { + desc2D.BindFlags |= D3D11_BIND_DEPTH_STENCIL; + } + + desc2D.Width = textureCreateInfo->width; + desc2D.Height = textureCreateInfo->height; + desc2D.ArraySize = textureCreateInfo->isCube ? 6 : textureCreateInfo->layerCount; + desc2D.CPUAccessFlags = 0; + desc2D.Format = format; + desc2D.MipLevels = textureCreateInfo->levelCount; + desc2D.MiscFlags = textureCreateInfo->isCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; + desc2D.SampleDesc.Count = 1; + desc2D.SampleDesc.Quality = 0; + desc2D.Usage = D3D11_USAGE_DEFAULT; + + res = ID3D11Device_CreateTexture2D( + renderer->device, + &desc2D, + NULL, + (ID3D11Texture2D**) &textureHandle + ); + ERROR_CHECK_RETURN("Could not create Texture2D", NULL); + + /* Create the SRV, if applicable */ + if (isSampler) + { + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + srvDesc.Format = D3D11_INTERNAL_GetSampleableFormat(format); + + if (textureCreateInfo->isCube) + { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + srvDesc.TextureCube.MipLevels = desc2D.MipLevels; + srvDesc.TextureCube.MostDetailedMip = 0; + } + else if (textureCreateInfo->layerCount > 1) + { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + srvDesc.Texture2DArray.MipLevels = desc2D.MipLevels; + srvDesc.Texture2DArray.MostDetailedMip = 0; + srvDesc.Texture2DArray.FirstArraySlice = 0; + srvDesc.Texture2DArray.ArraySize = textureCreateInfo->layerCount; + } + else + { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = desc2D.MipLevels; + srvDesc.Texture2D.MostDetailedMip = 0; + } + + res = ID3D11Device_CreateShaderResourceView( + renderer->device, + textureHandle, + &srvDesc, + &srv + ); + if (FAILED(res)) + { + ID3D11Resource_Release(textureHandle); + D3D11_INTERNAL_LogError(renderer->device, "Could not create SRV for 2D texture", res); + return NULL; + } + } + } + else + { + D3D11_TEXTURE3D_DESC desc3D; + + desc3D.BindFlags = 0; + if (isSampler || needSubresourceSRV) + { + desc3D.BindFlags |= D3D11_BIND_SHADER_RESOURCE; + } + if (needSubresourceUAV) + { + desc3D.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; + } + if (isColorTarget) + { + desc3D.BindFlags |= D3D11_BIND_RENDER_TARGET; + } + + desc3D.Width = textureCreateInfo->width; + desc3D.Height = textureCreateInfo->height; + desc3D.Depth = textureCreateInfo->depth; + desc3D.CPUAccessFlags = 0; + desc3D.Format = format; + desc3D.MipLevels = textureCreateInfo->levelCount; + desc3D.MiscFlags = 0; + desc3D.Usage = D3D11_USAGE_DEFAULT; + + res = ID3D11Device_CreateTexture3D( + renderer->device, + &desc3D, + NULL, + (ID3D11Texture3D**) &textureHandle + ); + ERROR_CHECK_RETURN("Could not create Texture3D", NULL); + + /* Create the SRV, if applicable */ + if (isSampler) + { + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + srvDesc.Format = format; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + srvDesc.Texture3D.MipLevels = desc3D.MipLevels; + srvDesc.Texture3D.MostDetailedMip = 0; + + res = ID3D11Device_CreateShaderResourceView( + renderer->device, + textureHandle, + &srvDesc, + &srv + ); + if (FAILED(res)) + { + ID3D11Resource_Release(textureHandle); + D3D11_INTERNAL_LogError(renderer->device, "Could not create SRV for 3D texture", res); + return NULL; + } + } + } + + d3d11Texture = (D3D11Texture*) SDL_malloc(sizeof(D3D11Texture)); + d3d11Texture->handle = textureHandle; + d3d11Texture->format = textureCreateInfo->format; + d3d11Texture->width = textureCreateInfo->width; + d3d11Texture->height = textureCreateInfo->height; + d3d11Texture->depth = textureCreateInfo->depth; + d3d11Texture->levelCount = textureCreateInfo->levelCount; + d3d11Texture->layerCount = textureCreateInfo->layerCount; + d3d11Texture->isCube = textureCreateInfo->isCube; + d3d11Texture->isRenderTarget = isColorTarget || isDepthStencil; + d3d11Texture->shaderView = srv; + + d3d11Texture->subresourceCount = d3d11Texture->levelCount * d3d11Texture->layerCount; + d3d11Texture->subresources = SDL_malloc( + d3d11Texture->subresourceCount * sizeof(D3D11TextureSubresource) + ); + + for (Uint32 layerIndex = 0; layerIndex < d3d11Texture->layerCount; layerIndex += 1) + { + for (Uint32 levelIndex = 0; levelIndex < d3d11Texture->levelCount; levelIndex += 1) + { + Uint32 subresourceIndex = D3D11_INTERNAL_CalcSubresource( + levelIndex, + layerIndex, + d3d11Texture->levelCount + ); + + d3d11Texture->subresources[subresourceIndex].parent = d3d11Texture; + d3d11Texture->subresources[subresourceIndex].layer = layerIndex; + d3d11Texture->subresources[subresourceIndex].level = levelIndex; + d3d11Texture->subresources[subresourceIndex].index = subresourceIndex; + + d3d11Texture->subresources[subresourceIndex].colorTargetView = NULL; + d3d11Texture->subresources[subresourceIndex].depthStencilTargetView = NULL; + d3d11Texture->subresources[subresourceIndex].srv = NULL; + d3d11Texture->subresources[subresourceIndex].uav = NULL; + d3d11Texture->subresources[subresourceIndex].msaaHandle = NULL; + d3d11Texture->subresources[subresourceIndex].msaaTargetView = NULL; + SDL_AtomicSet(&d3d11Texture->subresources[subresourceIndex].referenceCount, 0); + + if (isMultisample) + { + D3D11_TEXTURE2D_DESC desc2D; + + if (isColorTarget) + { + desc2D.BindFlags = D3D11_BIND_RENDER_TARGET; + } + else if (isDepthStencil) + { + desc2D.BindFlags = D3D11_BIND_DEPTH_STENCIL; + } + + desc2D.Width = textureCreateInfo->width; + desc2D.Height = textureCreateInfo->height; + desc2D.ArraySize = 1; + desc2D.CPUAccessFlags = 0; + desc2D.Format = format; + desc2D.MipLevels = 1; + desc2D.MiscFlags = 0; + desc2D.SampleDesc.Count = RefreshToD3D11_SampleCount[textureCreateInfo->sampleCount]; + desc2D.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN; + desc2D.Usage = D3D11_USAGE_DEFAULT; + + res = ID3D11Device_CreateTexture2D( + renderer->device, + &desc2D, + NULL, + (ID3D11Texture2D**) &d3d11Texture->subresources[subresourceIndex].msaaHandle + ); + ERROR_CHECK_RETURN("Could not create MSAA texture!", NULL); + + if (!isDepthStencil) + { + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + + rtvDesc.Format = format; + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; + + res = ID3D11Device_CreateRenderTargetView( + renderer->device, + d3d11Texture->subresources[subresourceIndex].msaaHandle, + &rtvDesc, + &d3d11Texture->subresources[subresourceIndex].msaaTargetView + ); + ERROR_CHECK_RETURN("Could not create MSAA RTV!", NULL); + } + } + + if (d3d11Texture->isRenderTarget) + { + if (isDepthStencil) + { + D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc; + + dsvDesc.Format = RefreshToD3D11_TextureFormat[d3d11Texture->format]; + dsvDesc.Flags = 0; + + if (isMultisample) + { + dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + } + else + { + dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + dsvDesc.Texture2D.MipSlice = levelIndex; + } + + res = ID3D11Device_CreateDepthStencilView( + renderer->device, + isMultisample ? d3d11Texture->subresources[subresourceIndex].msaaHandle : d3d11Texture->handle, + &dsvDesc, + &d3d11Texture->subresources[subresourceIndex].depthStencilTargetView + ); + ERROR_CHECK_RETURN("Could not create DSV!", NULL); + } + else + { + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + + rtvDesc.Format = RefreshToD3D11_TextureFormat[d3d11Texture->format]; + + if (d3d11Texture->layerCount > 1) + { + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.MipSlice = levelIndex; + rtvDesc.Texture2DArray.FirstArraySlice = layerIndex; + rtvDesc.Texture2DArray.ArraySize = 1; + } + else if (d3d11Texture->depth > 1) + { + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; + rtvDesc.Texture3D.MipSlice = levelIndex; + rtvDesc.Texture3D.FirstWSlice = 0; + rtvDesc.Texture3D.WSize = d3d11Texture->depth; + } + else + { + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtvDesc.Texture2D.MipSlice = levelIndex; + } + + res = ID3D11Device_CreateRenderTargetView( + renderer->device, + d3d11Texture->handle, + &rtvDesc, + &d3d11Texture->subresources[subresourceIndex].colorTargetView + ); + ERROR_CHECK_RETURN("Could not create RTV!", NULL); + } + } + + if (needSubresourceSRV) + { + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + + srvDesc.Format = format; + + if (d3d11Texture->layerCount > 1) + { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + srvDesc.Texture2DArray.ArraySize = 1; + srvDesc.Texture2DArray.FirstArraySlice = layerIndex; + srvDesc.Texture2DArray.MipLevels = 1; + srvDesc.Texture2DArray.MostDetailedMip = levelIndex; + } + else if (d3d11Texture->depth > 1) + { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + srvDesc.Texture3D.MipLevels = 1; + srvDesc.Texture3D.MostDetailedMip = levelIndex; + } + else + { + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = 1; + srvDesc.Texture2D.MostDetailedMip = levelIndex; + } + + res = ID3D11Device_CreateShaderResourceView( + renderer->device, + d3d11Texture->handle, + &srvDesc, + &d3d11Texture->subresources[subresourceIndex].srv + ); + ERROR_CHECK_RETURN("Could not create SRV!", NULL); + } + + if (needSubresourceUAV) + { + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; + uavDesc.Format = format; + + if (d3d11Texture->layerCount > 1) + { + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY; + uavDesc.Texture2DArray.MipSlice = levelIndex; + uavDesc.Texture2DArray.FirstArraySlice = layerIndex; + uavDesc.Texture2DArray.ArraySize = 1; + } + else if (d3d11Texture->depth > 1) + { + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D; + uavDesc.Texture3D.MipSlice = levelIndex; + uavDesc.Texture3D.FirstWSlice = 0; + uavDesc.Texture3D.WSize = d3d11Texture->layerCount; + } + else + { + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; + uavDesc.Texture2D.MipSlice = levelIndex; + } + + res = ID3D11Device_CreateUnorderedAccessView( + renderer->device, + d3d11Texture->handle, + &uavDesc, + &d3d11Texture->subresources[subresourceIndex].uav + ); + ERROR_CHECK_RETURN("Could not create UAV!", NULL); + } + } + } + + return d3d11Texture; +} + +static Refresh_Texture* D3D11_CreateTexture( + Refresh_Renderer *driverData, + Refresh_TextureCreateInfo *textureCreateInfo +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TextureContainer *container; + D3D11Texture *texture; + + texture = D3D11_INTERNAL_CreateTexture( + renderer, + textureCreateInfo + ); + + if (texture == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture!"); + return NULL; + } + + container = SDL_malloc(sizeof(D3D11TextureContainer)); + container->canBeCycled = 1; + container->createInfo = *textureCreateInfo; + container->activeTexture = texture; + container->textureCapacity = 1; + container->textureCount = 1; + container->textures = SDL_malloc( + container->textureCapacity * sizeof(D3D11Texture*) + ); + container->textures[0] = texture; + container->debugName = NULL; + + return (Refresh_Texture*) container; +} + +static void D3D11_INTERNAL_CycleActiveTexture( + D3D11Renderer *renderer, + D3D11TextureContainer *container +) { + for (Uint32 i = 0; i < container->textureCount; i += 1) + { + Uint32 refCountTotal = 0; + for (Uint32 j = 0; j < container->textures[i]->subresourceCount; j += 1) + { + refCountTotal += SDL_AtomicGet(&container->textures[i]->subresources[j].referenceCount); + } + + if (refCountTotal == 0) + { + container->activeTexture = container->textures[i]; + return; + } + } + + EXPAND_ARRAY_IF_NEEDED( + container->textures, + D3D11Texture*, + container->textureCount + 1, + container->textureCapacity, + container->textureCapacity + 1 + ); + + container->textures[container->textureCount] = D3D11_INTERNAL_CreateTexture( + renderer, + &container->createInfo + ); + container->textureCount += 1; + + container->activeTexture = container->textures[container->textureCount - 1]; + + if (renderer->debugMode && container->debugName != NULL) + { + D3D11_INTERNAL_SetTextureName( + renderer, + container->activeTexture, + container->debugName + ); + } +} + +static D3D11TextureSubresource* D3D11_INTERNAL_FetchTextureSubresource( + D3D11Texture *texture, + Uint32 layer, + Uint32 level +) { + Uint32 index = D3D11_INTERNAL_CalcSubresource(level, layer, texture->levelCount); + return &texture->subresources[index]; +} + +static D3D11TextureSubresource* D3D11_INTERNAL_PrepareTextureSubresourceForWrite( + D3D11Renderer *renderer, + D3D11TextureContainer *container, + Uint32 layer, + Uint32 level, + SDL_bool cycle +) { + D3D11TextureSubresource *subresource = D3D11_INTERNAL_FetchTextureSubresource( + container->activeTexture, + layer, + level + ); + + if ( + container->canBeCycled && + cycle && + SDL_AtomicGet(&subresource->referenceCount) > 0 + ) { + D3D11_INTERNAL_CycleActiveTexture( + renderer, + container + ); + + subresource = D3D11_INTERNAL_FetchTextureSubresource( + container->activeTexture, + layer, + level + ); + } + + return subresource; +} + +static D3D11Buffer* D3D11_INTERNAL_CreateBuffer( + D3D11Renderer *renderer, + D3D11_BUFFER_DESC *bufferDesc, + Uint32 sizeInBytes +) { + ID3D11Buffer *bufferHandle; + ID3D11UnorderedAccessView *uav = NULL; + ID3D11ShaderResourceView *srv = NULL; + D3D11Buffer *d3d11Buffer; + HRESULT res; + + /* Storage buffers have to be 4-aligned, so might as well align them all */ + sizeInBytes = D3D11_INTERNAL_NextHighestAlignment(sizeInBytes, 4); + + res = ID3D11Device_CreateBuffer( + renderer->device, + bufferDesc, + NULL, + &bufferHandle + ); + ERROR_CHECK_RETURN("Could not create buffer", NULL); + + /* Storage buffer */ + if (bufferDesc->MiscFlags & D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS) + { + /* Create a UAV for the buffer */ + + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; + uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.NumElements = sizeInBytes / sizeof(Uint32); + uavDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW; + + res = ID3D11Device_CreateUnorderedAccessView( + renderer->device, + (ID3D11Resource*) bufferHandle, + &uavDesc, + &uav + ); + if (FAILED(res)) + { + ID3D11Buffer_Release(bufferHandle); + ERROR_CHECK_RETURN("Could not create UAV for buffer!", NULL); + } + + /* Create a SRV for the buffer */ + + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + srvDesc.Format = DXGI_FORMAT_R32_TYPELESS; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX; + srvDesc.BufferEx.FirstElement = 0; + srvDesc.BufferEx.NumElements = sizeInBytes / sizeof(Uint32); + srvDesc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW; + + res = ID3D11Device_CreateShaderResourceView( + renderer->device, + (ID3D11Resource*) bufferHandle, + &srvDesc, + &srv + ); + if (FAILED(res)) + { + ID3D11Buffer_Release(bufferHandle); + ERROR_CHECK_RETURN("Could not create SRV for buffer!", NULL); + } + } + + d3d11Buffer = SDL_malloc(sizeof(D3D11Buffer)); + d3d11Buffer->handle = bufferHandle; + d3d11Buffer->size = sizeInBytes; + d3d11Buffer->uav = uav; + d3d11Buffer->srv = srv; + SDL_AtomicSet(&d3d11Buffer->referenceCount, 0); + + return d3d11Buffer; +} + +static Refresh_Buffer* D3D11_CreateBuffer( + Refresh_Renderer *driverData, + Refresh_BufferUsageFlags usageFlags, + Uint32 sizeInBytes +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11BufferContainer *container; + D3D11Buffer *buffer; + D3D11_BUFFER_DESC bufferDesc; + + bufferDesc.BindFlags = 0; + if (usageFlags & REFRESH_BUFFERUSAGE_VERTEX_BIT) + { + bufferDesc.BindFlags |= D3D11_BIND_VERTEX_BUFFER; + } + if (usageFlags & REFRESH_BUFFERUSAGE_INDEX_BIT) + { + bufferDesc.BindFlags |= D3D11_BIND_INDEX_BUFFER; + } + if (usageFlags & REFRESH_BUFFERUSAGE_INDIRECT_BIT) + { + bufferDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; + } + + if (usageFlags & ( + REFRESH_BUFFERUSAGE_GRAPHICS_STORAGE_READ_BIT | + REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_READ_BIT | + REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_WRITE_BIT + )) { + bufferDesc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; + } + + bufferDesc.ByteWidth = sizeInBytes; + bufferDesc.Usage = D3D11_USAGE_DEFAULT; + bufferDesc.CPUAccessFlags = 0; + bufferDesc.StructureByteStride = 0; + bufferDesc.MiscFlags = 0; + + if (usageFlags & REFRESH_BUFFERUSAGE_INDIRECT_BIT) + { + bufferDesc.MiscFlags |= D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS; + } + if (usageFlags & ( + REFRESH_BUFFERUSAGE_GRAPHICS_STORAGE_READ_BIT | + REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_READ_BIT | + REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_WRITE_BIT + )) + { + bufferDesc.MiscFlags |= D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; + } + + buffer = D3D11_INTERNAL_CreateBuffer( + renderer, + &bufferDesc, + sizeInBytes + ); + + if (buffer == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create buffer!"); + return NULL; + } + + container = SDL_malloc(sizeof(D3D11BufferContainer)); + container->activeBuffer = buffer; + container->bufferCapacity = 1; + container->bufferCount = 1; + container->buffers = SDL_malloc( + container->bufferCapacity * sizeof(D3D11Buffer*) + ); + container->buffers[0] = container->activeBuffer; + container->bufferDesc = bufferDesc; + container->debugName = NULL; + + return (Refresh_Buffer*) container; +} + +static D3D11UniformBuffer *D3D11_INTERNAL_CreateUniformBuffer( + D3D11Renderer *renderer, + Uint32 sizeInBytes +) { + D3D11UniformBuffer *uniformBuffer; + D3D11BufferContainer *container; + D3D11Buffer *buffer; + D3D11_BUFFER_DESC bufferDesc; + + bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + bufferDesc.ByteWidth = sizeInBytes; + bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + bufferDesc.MiscFlags = 0; + bufferDesc.StructureByteStride = 0; + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + + buffer = D3D11_INTERNAL_CreateBuffer( + renderer, + &bufferDesc, + sizeInBytes + ); + + if (buffer == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create UniformBuffer!"); + return NULL; + } + + container = SDL_malloc(sizeof(D3D11BufferContainer)); + container->activeBuffer = buffer; + container->bufferCapacity = 1; + container->bufferCount = 1; + container->buffers = SDL_malloc( + container->bufferCapacity * sizeof(D3D11Buffer*) + ); + container->buffers[0] = container->activeBuffer; + container->bufferDesc = bufferDesc; + container->debugName = NULL; + + uniformBuffer = SDL_malloc(sizeof(D3D11UniformBuffer)); + uniformBuffer->bufferContainer = container; + uniformBuffer->offset = 0; + uniformBuffer->currentBlockSize = 0; + + return uniformBuffer; +} + +static void D3D11_INTERNAL_CycleActiveBuffer( + D3D11Renderer *renderer, + D3D11BufferContainer *container +) { + Uint32 size = container->activeBuffer->size; + + for (Uint32 i = 0; i < container->bufferCount; i += 1) + { + if (SDL_AtomicGet(&container->buffers[i]->referenceCount) == 0) + { + container->activeBuffer = container->buffers[i]; + return; + } + } + + EXPAND_ARRAY_IF_NEEDED( + container->buffers, + D3D11Buffer*, + container->bufferCount + 1, + container->bufferCapacity, + container->bufferCapacity + 1 + ); + + container->buffers[container->bufferCount] = D3D11_INTERNAL_CreateBuffer( + renderer, + &container->bufferDesc, + size + ); + container->bufferCount += 1; + + container->activeBuffer = container->buffers[container->bufferCount - 1]; + + if (renderer->debugMode && container->debugName != NULL) + { + D3D11_INTERNAL_SetBufferName( + renderer, + container->activeBuffer, + container->debugName + ); + } +} + +static D3D11Buffer* D3D11_INTERNAL_PrepareBufferForWrite( + D3D11Renderer *renderer, + D3D11BufferContainer *container, + SDL_bool cycle +) { + if ( + cycle && + SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0 + ) { + D3D11_INTERNAL_CycleActiveBuffer( + renderer, + container + ); + } + + return container->activeBuffer; +} + +static D3D11TransferBuffer* D3D11_INTERNAL_CreateTransferBuffer( + D3D11Renderer *renderer, + Refresh_TransferUsage usage, + Refresh_TransferBufferMapFlags mapFlags, + Uint32 sizeInBytes +) { + D3D11TransferBuffer *transferBuffer = SDL_malloc(sizeof(D3D11TransferBuffer)); + UINT cpuAccessFlags = 0; + + transferBuffer->size = sizeInBytes; + SDL_AtomicSet(&transferBuffer->referenceCount, 0); + + if (mapFlags & REFRESH_TRANSFER_MAP_READ) + { + cpuAccessFlags |= D3D11_CPU_ACCESS_READ; + } + + if (mapFlags & REFRESH_TRANSFER_MAP_WRITE) + { + cpuAccessFlags |= D3D11_CPU_ACCESS_WRITE; + } + + if (usage == REFRESH_TRANSFERUSAGE_BUFFER) + { + D3D11_BUFFER_DESC stagingBufferDesc; + HRESULT res; + + stagingBufferDesc.ByteWidth = sizeInBytes; + stagingBufferDesc.Usage = D3D11_USAGE_STAGING; + stagingBufferDesc.BindFlags = 0; + stagingBufferDesc.CPUAccessFlags = cpuAccessFlags; + stagingBufferDesc.MiscFlags = 0; + stagingBufferDesc.StructureByteStride = 0; + + res = ID3D11Device_CreateBuffer( + renderer->device, + &stagingBufferDesc, + NULL, + &transferBuffer->bufferTransfer.stagingBuffer + ); + ERROR_CHECK_RETURN("Could not create staging buffer", NULL); + } + else /* TRANSFERUSAGE_TEXTURE */ + { + transferBuffer->textureTransfer.data = (Uint8*) SDL_malloc(sizeInBytes); + transferBuffer->textureTransfer.downloadTexture = NULL; + } + + return transferBuffer; +} + +/* This actually returns a container handle so we can rotate buffers on Cycle. */ +static Refresh_TransferBuffer* D3D11_CreateTransferBuffer( + Refresh_Renderer *driverData, + Refresh_TransferUsage usage, + Refresh_TransferBufferMapFlags mapFlags, + Uint32 sizeInBytes +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) SDL_malloc(sizeof(D3D11TransferBufferContainer)); + + container->usage = usage; + container->mapFlags = mapFlags; + container->bufferCapacity = 1; + container->bufferCount = 1; + container->buffers = SDL_malloc( + container->bufferCapacity * sizeof(D3D11TransferBuffer*) + ); + + container->buffers[0] = D3D11_INTERNAL_CreateTransferBuffer( + renderer, + usage, + mapFlags, + sizeInBytes + ); + + container->activeBuffer = container->buffers[0]; + + return (Refresh_TransferBuffer*) container; +} + +/* TransferBuffer Data */ + +static void D3D11_INTERNAL_CycleActiveTransferBuffer( + D3D11Renderer *renderer, + D3D11TransferBufferContainer *container +) { + Uint32 size = container->activeBuffer->size; + + for (Uint32 i = 0; i < container->bufferCount; i += 1) + { + if (SDL_AtomicGet(&container->buffers[i]->referenceCount) == 0) + { + container->activeBuffer = container->buffers[i]; + return; + } + } + + EXPAND_ARRAY_IF_NEEDED( + container->buffers, + D3D11TransferBuffer*, + container->bufferCount + 1, + container->bufferCapacity, + container->bufferCapacity + 1 + ); + + container->buffers[container->bufferCount] = D3D11_INTERNAL_CreateTransferBuffer( + renderer, + container->usage, + container->mapFlags, + size + ); + container->bufferCount += 1; + + container->activeBuffer = container->buffers[container->bufferCount - 1]; +} + +static void D3D11_MapTransferBuffer( + Refresh_Renderer *driverData, + Refresh_TransferBuffer *transferBuffer, + SDL_bool cycle, + void **ppData +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *buffer = container->activeBuffer; + D3D11_MAPPED_SUBRESOURCE mappedSubresource; + HRESULT res; + + /* Rotate the transfer buffer if necessary */ + if ( + cycle && + SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0 + ) { + D3D11_INTERNAL_CycleActiveTransferBuffer( + renderer, + container + ); + buffer = container->activeBuffer; + } + + if (container->usage == REFRESH_TRANSFERUSAGE_BUFFER) + { + SDL_LockMutex(renderer->contextLock); + res = ID3D11DeviceContext_Map( + renderer->immediateContext, + (ID3D11Resource*) buffer->bufferTransfer.stagingBuffer, + 0, + D3D11_MAP_WRITE, + 0, + &mappedSubresource + ); + SDL_UnlockMutex(renderer->contextLock); + + ERROR_CHECK_RETURN("Failed to map staging buffer", ); + + *ppData = (Uint8*) mappedSubresource.pData; + } + else /* TEXTURE */ + { + *ppData = buffer->textureTransfer.data; + } +} + +static void D3D11_UnmapTransferBuffer( + Refresh_Renderer *driverData, + Refresh_TransferBuffer *transferBuffer +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *buffer = container->activeBuffer; + + if (container->usage == REFRESH_TRANSFERUSAGE_BUFFER) + { + SDL_LockMutex(renderer->contextLock); + ID3D11DeviceContext_Unmap( + renderer->immediateContext, + (ID3D11Resource*) buffer->bufferTransfer.stagingBuffer, + 0 + ); + SDL_UnlockMutex(renderer->contextLock); + } + + /* TEXTURE unmap is a no-op */ +} + +static void D3D11_SetTransferData( + Refresh_Renderer *driverData, + void* data, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *buffer = container->activeBuffer; + HRESULT res; + + /* Rotate the transfer buffer if necessary */ + if ( + cycle && + SDL_AtomicGet(&container->activeBuffer->referenceCount) > 0 + ) { + D3D11_INTERNAL_CycleActiveTransferBuffer( + renderer, + container + ); + buffer = container->activeBuffer; + } + + if (container->usage == REFRESH_TRANSFERUSAGE_BUFFER) + { + D3D11_MAPPED_SUBRESOURCE mappedSubresource; + + SDL_LockMutex(renderer->contextLock); + res = ID3D11DeviceContext_Map( + renderer->immediateContext, + (ID3D11Resource*) buffer->bufferTransfer.stagingBuffer, + 0, + D3D11_MAP_WRITE, + 0, + &mappedSubresource + ); + ERROR_CHECK_RETURN("Failed to map staging buffer", ); + + SDL_memcpy( + ((Uint8*) mappedSubresource.pData) + copyParams->dstOffset, + ((Uint8*) data) + copyParams->srcOffset, + copyParams->size + ); + + ID3D11DeviceContext_Unmap( + renderer->immediateContext, + (ID3D11Resource*) buffer->bufferTransfer.stagingBuffer, + 0 + ); + SDL_UnlockMutex(renderer->contextLock); + } + else /* TEXTURE */ + { + SDL_memcpy( + buffer->textureTransfer.data + copyParams->dstOffset, + ((Uint8*) data) + copyParams->srcOffset, + copyParams->size + ); + } +} + +static void D3D11_GetTransferData( + Refresh_Renderer *driverData, + Refresh_TransferBuffer *transferBuffer, + void* data, + Refresh_BufferCopy *copyParams +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *buffer = container->activeBuffer; + D3D11_MAPPED_SUBRESOURCE subresource; + Uint8* dataPtr; + Uint32 dataPtrOffset; + Uint32 depth, row, copySize; + HRESULT res; + + if (container->usage == REFRESH_TRANSFERUSAGE_BUFFER) + { + SDL_LockMutex(renderer->contextLock); + res = ID3D11DeviceContext_Map( + renderer->immediateContext, + (ID3D11Resource*) buffer->bufferTransfer.stagingBuffer, + 0, + D3D11_MAP_READ, + 0, + &subresource + ); + ERROR_CHECK_RETURN("Failed to map staging buffer", ); + + SDL_memcpy( + ((Uint8*) data) + copyParams->dstOffset, + ((Uint8*) subresource.pData) + copyParams->srcOffset, + copyParams->size + ); + + ID3D11DeviceContext_Unmap( + renderer->immediateContext, + (ID3D11Resource*) buffer->bufferTransfer.stagingBuffer, + 0 + ); + SDL_UnlockMutex(renderer->contextLock); + } + else /* TEXTURE */ + { + SDL_LockMutex(renderer->contextLock); + + /* Read from the staging texture */ + res = ID3D11DeviceContext_Map( + renderer->immediateContext, + buffer->textureTransfer.downloadTexture, + 0, + D3D11_MAP_READ, + 0, + &subresource + ); + ERROR_CHECK_RETURN("Could not map texture for reading",) + + dataPtr = (Uint8*) data; + dataPtrOffset = copyParams->dstOffset; + + if (buffer->textureTransfer.downloadTightlyPacked) + { + SDL_memcpy( + dataPtr + dataPtrOffset, + (Uint8*) subresource.pData + copyParams->srcOffset, + SDL_min(copyParams->size, buffer->textureTransfer.downloadDepth * buffer->textureTransfer.downloadHeight * buffer->textureTransfer.downloadBytesPerRow) + ); + } + else + { + for (depth = 0; depth < buffer->textureTransfer.downloadDepth; depth += 1) + { + for (row = 0; row < buffer->textureTransfer.downloadHeight; row += 1) + { + copySize = SDL_min(copyParams->size - dataPtrOffset, buffer->textureTransfer.downloadBytesPerRow); + + if (copySize == 0) + { + goto unmap; + } + + SDL_memcpy( + dataPtr + dataPtrOffset, + (Uint8*) subresource.pData + copyParams->srcOffset + (depth * buffer->textureTransfer.downloadBytesPerDepthSlice) + (row * buffer->textureTransfer.downloadBytesPerRow), + copySize + ); + dataPtrOffset += copySize; + } + } + } + + unmap: + ID3D11DeviceContext1_Unmap( + renderer->immediateContext, + buffer->textureTransfer.downloadTexture, + 0 + ); + + SDL_UnlockMutex(renderer->contextLock); + } +} + +/* Copy Pass */ + +static void D3D11_BeginCopyPass( + Refresh_CommandBuffer *commandBuffer +) { + /* no-op */ +} + +static void D3D11_UploadToTexture( + Refresh_CommandBuffer *commandBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_TextureRegion *textureRegion, + Refresh_BufferImageCopy *copyParams, + SDL_bool cycle +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer; + D3D11TransferBufferContainer *transferContainer = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *d3d11TransferBuffer = transferContainer->activeBuffer; + D3D11TextureContainer *d3d11TextureContainer = (D3D11TextureContainer*) textureRegion->textureSlice.texture; + Uint32 bufferStride = copyParams->bufferStride; + Uint32 bufferImageHeight = copyParams->bufferImageHeight; + Sint32 w = textureRegion->w; + Sint32 h = textureRegion->h; + D3D11TextureContainer *stagingTexture; + Refresh_TextureCreateInfo stagingTextureCreateInfo; + + D3D11TextureSubresource *textureSubresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite( + renderer, + d3d11TextureContainer, + textureRegion->textureSlice.layer, + textureRegion->textureSlice.mipLevel, + cycle); + + Sint32 blockSize = Texture_GetBlockSize(textureSubresource->parent->format); + if (blockSize > 1) { + w = (w + blockSize - 1) & ~(blockSize - 1); + h = (h + blockSize - 1) & ~(blockSize - 1); + } + + if (bufferStride == 0 || bufferImageHeight == 0) + { + bufferStride = BytesPerRow(w, textureSubresource->parent->format); + bufferImageHeight = h * Refresh_TextureFormatTexelBlockSize(textureSubresource->parent->format); + } + + D3D11_BOX stagingTextureBox; + stagingTextureBox.left = 0; + stagingTextureBox.top = 0; + stagingTextureBox.front = 0; + stagingTextureBox.right = w; + stagingTextureBox.bottom = h; + stagingTextureBox.back = textureRegion->d; + + /* UpdateSubresource1 is completely busted on AMD, it truncates after X bytes. + * So we get to do this Fun (Tm) workaround where we create a staging texture + * and upload to it in the immediate context before using a copy command. + */ + + stagingTextureCreateInfo.width = w; + stagingTextureCreateInfo.height = h; + stagingTextureCreateInfo.depth = textureRegion->d; + stagingTextureCreateInfo.layerCount = 1; + stagingTextureCreateInfo.levelCount = 1; + stagingTextureCreateInfo.isCube = 0; + stagingTextureCreateInfo.usageFlags = 0; + stagingTextureCreateInfo.sampleCount = REFRESH_SAMPLECOUNT_1; + stagingTextureCreateInfo.format = ((D3D11TextureContainer *)textureRegion->textureSlice.texture)->createInfo.format; + + stagingTexture = (D3D11TextureContainer*) D3D11_CreateTexture( + (Refresh_Renderer*) d3d11CommandBuffer->renderer, + &stagingTextureCreateInfo + ); + + if (stagingTexture == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Staging texture creation failed"); + } + + SDL_LockMutex(renderer->contextLock); + ID3D11DeviceContext_UpdateSubresource( + renderer->immediateContext, + stagingTexture->activeTexture->handle, + 0, + &stagingTextureBox, + (Uint8*) d3d11TransferBuffer->textureTransfer.data + copyParams->bufferOffset, + bufferStride, + bufferStride * bufferImageHeight + ); + SDL_UnlockMutex(renderer->contextLock); + + ID3D11DeviceContext1_CopySubresourceRegion1( + d3d11CommandBuffer->context, + textureSubresource->parent->handle, + textureSubresource->index, + textureRegion->x, + textureRegion->y, + textureRegion->z, + stagingTexture->activeTexture->handle, + 0, + &stagingTextureBox, + D3D11_COPY_NO_OVERWRITE + ); + + /* Clean up the staging texture */ + D3D11_ReleaseTexture( + (Refresh_Renderer*) d3d11CommandBuffer->renderer, + (Refresh_Texture *)stagingTexture + ); + + D3D11_INTERNAL_TrackTextureSubresource(d3d11CommandBuffer, &stagingTexture->activeTexture->subresources[0]); + D3D11_INTERNAL_TrackTextureSubresource(d3d11CommandBuffer, textureSubresource); + D3D11_INTERNAL_TrackTransferBuffer(d3d11CommandBuffer, d3d11TransferBuffer); +} + +static void D3D11_UploadToBuffer( + Refresh_CommandBuffer *commandBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_Buffer *buffer, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer; + D3D11TransferBufferContainer *transferContainer = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *d3d11TransferBuffer = transferContainer->activeBuffer; + D3D11BufferContainer *bufferContainer = (D3D11BufferContainer*) buffer; + D3D11_BOX srcBox = { copyParams->srcOffset, 0, 0, copyParams->srcOffset + copyParams->size, 1, 1 }; + + D3D11Buffer *d3d11Buffer = D3D11_INTERNAL_PrepareBufferForWrite( + renderer, + bufferContainer, + cycle + ); + + ID3D11DeviceContext1_CopySubresourceRegion1( + d3d11CommandBuffer->context, + (ID3D11Resource*) d3d11Buffer->handle, + 0, + copyParams->dstOffset, + 0, + 0, + (ID3D11Resource*) d3d11TransferBuffer->bufferTransfer.stagingBuffer, + 0, + &srcBox, + D3D11_COPY_NO_OVERWRITE /* always no overwrite because we manually discard */ + ); + + D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer); + D3D11_INTERNAL_TrackTransferBuffer(d3d11CommandBuffer, d3d11TransferBuffer); +} + +static void D3D11_DownloadFromTexture( + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *textureRegion, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferImageCopy *copyParams +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Renderer *renderer = d3d11CommandBuffer->renderer; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *d3d11TransferBuffer = container->activeBuffer; + D3D11TextureContainer *d3d11TextureContainer = (D3D11TextureContainer*) textureRegion->textureSlice.texture; + D3D11_TEXTURE2D_DESC stagingDesc2D; + D3D11_TEXTURE3D_DESC stagingDesc3D; + D3D11TextureSubresource *textureSubresource = D3D11_INTERNAL_FetchTextureSubresource( + d3d11TextureContainer->activeTexture, + textureRegion->textureSlice.layer, + textureRegion->textureSlice.mipLevel + ); + Uint32 bufferStride = copyParams->bufferStride; + Uint32 bufferImageHeight = copyParams->bufferImageHeight; + Uint32 bytesPerRow, bytesPerDepthSlice; + D3D11_BOX srcBox = {textureRegion->x, textureRegion->y, textureRegion->z, textureRegion->x + textureRegion->w, textureRegion->y + textureRegion->h, 1}; + HRESULT res; + + if (bufferStride == 0 || bufferImageHeight == 0) + { + bufferStride = textureRegion->w; + bufferImageHeight = textureRegion->h; + } + + bytesPerRow = BytesPerRow(bufferStride, textureSubresource->parent->format); + bytesPerDepthSlice = bytesPerRow * bufferImageHeight; + + if (d3d11TransferBuffer->textureTransfer.downloadTexture != NULL) + { + ID3D11Resource_Release(d3d11TransferBuffer->textureTransfer.downloadTexture); + } + + if (textureRegion->d == 1) + { + stagingDesc2D.Width = textureRegion->w; + stagingDesc2D.Height = textureRegion->h; + stagingDesc2D.MipLevels = 1; + stagingDesc2D.ArraySize = 1; + stagingDesc2D.Format = RefreshToD3D11_TextureFormat[textureSubresource->parent->format]; + stagingDesc2D.SampleDesc.Count = 1; + stagingDesc2D.SampleDesc.Quality = 0; + stagingDesc2D.Usage = D3D11_USAGE_STAGING; + stagingDesc2D.BindFlags = 0; + stagingDesc2D.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingDesc2D.MiscFlags = 0; + + res = ID3D11Device_CreateTexture2D( + renderer->device, + &stagingDesc2D, + NULL, + (ID3D11Texture2D**) &d3d11TransferBuffer->textureTransfer.downloadTexture + ); + ERROR_CHECK_RETURN("Staging texture creation failed",) + } + else + { + stagingDesc3D.Width = textureRegion->w; + stagingDesc3D.Height = textureRegion->h; + stagingDesc3D.Depth = textureRegion->d; + stagingDesc3D.MipLevels = 1; + stagingDesc3D.Format = RefreshToD3D11_TextureFormat[textureSubresource->parent->format]; + stagingDesc3D.Usage = D3D11_USAGE_STAGING; + stagingDesc3D.BindFlags = 0; + stagingDesc3D.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingDesc3D.MiscFlags = 0; + + res = ID3D11Device_CreateTexture3D( + renderer->device, + &stagingDesc3D, + NULL, + (ID3D11Texture3D**) &d3d11TransferBuffer->textureTransfer.downloadTexture + ); + } + + d3d11TransferBuffer->textureTransfer.downloadWidth = textureRegion->w; + d3d11TransferBuffer->textureTransfer.downloadHeight = textureRegion->h; + d3d11TransferBuffer->textureTransfer.downloadDepth = textureRegion->d; + d3d11TransferBuffer->textureTransfer.downloadBytesPerRow = bytesPerRow; + d3d11TransferBuffer->textureTransfer.downloadBytesPerDepthSlice = bytesPerDepthSlice; + d3d11TransferBuffer->textureTransfer.downloadTightlyPacked = textureRegion->w == bufferStride && textureRegion->h == bufferImageHeight; + + ID3D11DeviceContext1_CopySubresourceRegion1( + d3d11CommandBuffer->context, + d3d11TransferBuffer->textureTransfer.downloadTexture, + 0, + 0, + 0, + 0, + textureSubresource->parent->handle, + textureSubresource->index, + &srcBox, + D3D11_COPY_NO_OVERWRITE + ); +} + +static void D3D11_DownloadFromBuffer( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *buffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11TransferBufferContainer *container = (D3D11TransferBufferContainer*) transferBuffer; + D3D11TransferBuffer *d3d11TransferBuffer = container->activeBuffer; + D3D11BufferContainer *d3d11BufferContainer = (D3D11BufferContainer*) buffer; + D3D11_BOX srcBox = { copyParams->srcOffset, 0, 0, copyParams->size, 1, 1 }; + + ID3D11DeviceContext1_CopySubresourceRegion1( + d3d11CommandBuffer->context, + (ID3D11Resource*) d3d11TransferBuffer->bufferTransfer.stagingBuffer, + 0, + copyParams->dstOffset, + 0, + 0, + (ID3D11Resource*) d3d11BufferContainer->activeBuffer->handle, + 0, + &srcBox, + D3D11_COPY_NO_OVERWRITE + ); +} + +static void D3D11_CopyTextureToTexture( + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *source, + Refresh_TextureRegion *destination, + SDL_bool cycle +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer; + D3D11TextureContainer *srcContainer = (D3D11TextureContainer*) source->textureSlice.texture; + D3D11TextureContainer *dstContainer = (D3D11TextureContainer*) destination->textureSlice.texture; + + D3D11_BOX srcBox = { source->x, source->y, source->z, source->x + source->w, source->y + source->h, 1 }; + + D3D11TextureSubresource *srcSubresource = D3D11_INTERNAL_FetchTextureSubresource( + srcContainer->activeTexture, + source->textureSlice.layer, + source->textureSlice.mipLevel + ); + + D3D11TextureSubresource *dstSubresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite( + renderer, + dstContainer, + destination->textureSlice.layer, + destination->textureSlice.mipLevel, + cycle + ); + + ID3D11DeviceContext1_CopySubresourceRegion1( + d3d11CommandBuffer->context, + dstSubresource->parent->handle, + dstSubresource->index, + destination->x, + destination->y, + destination->z, + srcSubresource->parent->handle, + srcSubresource->index, + &srcBox, + D3D11_COPY_NO_OVERWRITE + ); + + D3D11_INTERNAL_TrackTextureSubresource(d3d11CommandBuffer, srcSubresource); + D3D11_INTERNAL_TrackTextureSubresource(d3d11CommandBuffer, dstSubresource); +} + +static void D3D11_CopyBufferToBuffer( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *source, + Refresh_Buffer *destination, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer; + D3D11BufferContainer *srcBufferContainer = (D3D11BufferContainer*) source; + D3D11BufferContainer *dstBufferContainer = (D3D11BufferContainer*) destination; + D3D11_BOX srcBox = { copyParams->srcOffset, 0, 0, copyParams->srcOffset + copyParams->size, 1, 1 }; + + D3D11Buffer *srcBuffer = srcBufferContainer->activeBuffer; + D3D11Buffer *dstBuffer = D3D11_INTERNAL_PrepareBufferForWrite( + renderer, + dstBufferContainer, + cycle + ); + + ID3D11DeviceContext1_CopySubresourceRegion1( + d3d11CommandBuffer->context, + (ID3D11Resource*) dstBuffer->handle, + 0, + copyParams->dstOffset, + 0, + 0, + (ID3D11Resource*) srcBuffer->handle, + 0, + &srcBox, + D3D11_COPY_NO_OVERWRITE /* always no overwrite because we either manually discard or the write is unsafe */ + ); + + D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, srcBuffer); + D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, dstBuffer); +} + +static void D3D11_GenerateMipmaps( + Refresh_CommandBuffer *commandBuffer, + Refresh_Texture *texture +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11TextureContainer *d3d11TextureContainer = (D3D11TextureContainer*) texture; + + ID3D11DeviceContext1_GenerateMips( + d3d11CommandBuffer->context, + d3d11TextureContainer->activeTexture->shaderView + ); + + for (Uint32 i = 0; i < d3d11TextureContainer->activeTexture->subresourceCount; i += 1) + { + D3D11_INTERNAL_TrackTextureSubresource( + d3d11CommandBuffer, + &d3d11TextureContainer->activeTexture->subresources[i] + ); + } +} + +static void D3D11_EndCopyPass( + Refresh_CommandBuffer *commandBuffer +) { + /* no-op */ +} + +/* Uniforms */ + +/* TODO: we could get a big performance boost by storing data and mapping right before submitting commands */ +static void D3D11_INTERNAL_SetUniformBufferData( + D3D11Renderer *renderer, + D3D11CommandBuffer *commandBuffer, + D3D11Buffer *uniformBuffer, + Uint32 offset, + void* data, + Uint32 dataLength +) { + D3D11_MAPPED_SUBRESOURCE subres; + + HRESULT res = ID3D11DeviceContext_Map( + commandBuffer->context, + (ID3D11Resource*) uniformBuffer->handle, + 0, + offset == 0 ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE, + 0, + &subres + ); + ERROR_CHECK_RETURN("Could not map buffer for writing!", ); + + SDL_memcpy( + (Uint8*) subres.pData + offset, + data, + dataLength + ); + + ID3D11DeviceContext_Unmap( + commandBuffer->context, + (ID3D11Resource*) uniformBuffer->handle, + 0 + ); +} + +/* Graphics State */ + +static void D3D11_INTERNAL_AllocateCommandBuffers( + D3D11Renderer *renderer, + Uint32 allocateCount +) { + D3D11CommandBuffer *commandBuffer; + HRESULT res; + + renderer->availableCommandBufferCapacity += allocateCount; + + renderer->availableCommandBuffers = SDL_realloc( + renderer->availableCommandBuffers, + sizeof(D3D11CommandBuffer*) * renderer->availableCommandBufferCapacity + ); + + for (Uint32 i = 0; i < allocateCount; i += 1) + { + commandBuffer = SDL_malloc(sizeof(D3D11CommandBuffer)); + commandBuffer->renderer = renderer; + + /* Deferred Device Context */ + res = ID3D11Device1_CreateDeferredContext1( + renderer->device, + 0, + &commandBuffer->context + ); + ERROR_CHECK("Could not create deferred context"); + + commandBuffer->initializedVertexUniformBufferCount = 0; + commandBuffer->initializedFragmentUniformBufferCount = 0; + commandBuffer->initializedComputeUniformBufferCount = 0; + + commandBuffer->windowDataCapacity = 1; + commandBuffer->windowDataCount = 0; + commandBuffer->windowDatas = SDL_malloc( + commandBuffer->windowDataCapacity * sizeof(D3D11WindowData*) + ); + + /* Reference Counting */ + commandBuffer->usedBufferCapacity = 4; + commandBuffer->usedBufferCount = 0; + commandBuffer->usedBuffers = SDL_malloc( + commandBuffer->usedBufferCapacity * sizeof(D3D11Buffer*) + ); + + commandBuffer->usedTransferBufferCapacity = 4; + commandBuffer->usedTransferBufferCount = 0; + commandBuffer->usedTransferBuffers = SDL_malloc( + commandBuffer->usedTransferBufferCapacity * sizeof(D3D11TransferBuffer*) + ); + + commandBuffer->usedTextureSubresourceCapacity = 4; + commandBuffer->usedTextureSubresourceCount = 0; + commandBuffer->usedTextureSubresources = SDL_malloc( + commandBuffer->usedTextureSubresourceCapacity * sizeof(D3D11TextureSubresource*) + ); + + renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer; + renderer->availableCommandBufferCount += 1; + } +} + +static D3D11CommandBuffer* D3D11_INTERNAL_GetInactiveCommandBufferFromPool( + D3D11Renderer *renderer +) { + D3D11CommandBuffer *commandBuffer; + + if (renderer->availableCommandBufferCount == 0) + { + D3D11_INTERNAL_AllocateCommandBuffers( + renderer, + renderer->availableCommandBufferCapacity + ); + } + + commandBuffer = renderer->availableCommandBuffers[renderer->availableCommandBufferCount - 1]; + renderer->availableCommandBufferCount -= 1; + + return commandBuffer; +} + +static Uint8 D3D11_INTERNAL_CreateFence( + D3D11Renderer *renderer +) { + D3D11_QUERY_DESC queryDesc; + ID3D11Query *queryHandle; + D3D11Fence* fence; + HRESULT res; + + queryDesc.Query = D3D11_QUERY_EVENT; + queryDesc.MiscFlags = 0; + res = ID3D11Device_CreateQuery( + renderer->device, + &queryDesc, + &queryHandle + ); + ERROR_CHECK_RETURN("Could not create query", 0); + + fence = SDL_malloc(sizeof(D3D11Fence)); + fence->handle = queryHandle; + SDL_AtomicSet(&fence->referenceCount, 0); + + /* Add it to the available pool */ + if (renderer->availableFenceCount >= renderer->availableFenceCapacity) + { + renderer->availableFenceCapacity *= 2; + renderer->availableFences = SDL_realloc( + renderer->availableFences, + sizeof(D3D11Fence*) * renderer->availableFenceCapacity + ); + } + + renderer->availableFences[renderer->availableFenceCount] = fence; + renderer->availableFenceCount += 1; + + return 1; +} + +static Uint8 D3D11_INTERNAL_AcquireFence( + D3D11CommandBuffer *commandBuffer +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer; + D3D11Fence *fence; + + /* Acquire a fence from the pool */ + SDL_LockMutex(renderer->fenceLock); + + if (renderer->availableFenceCount == 0) + { + if (!D3D11_INTERNAL_CreateFence(renderer)) + { + SDL_UnlockMutex(renderer->fenceLock); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create fence!"); + return 0; + } + } + + fence = renderer->availableFences[renderer->availableFenceCount - 1]; + renderer->availableFenceCount -= 1; + + SDL_UnlockMutex(renderer->fenceLock); + + /* Associate the fence with the command buffer */ + commandBuffer->fence = fence; + (void)SDL_AtomicIncRef(&commandBuffer->fence->referenceCount); + + return 1; +} + +static Refresh_CommandBuffer* D3D11_AcquireCommandBuffer( + Refresh_Renderer *driverData +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *commandBuffer; + Uint32 i; + + SDL_LockMutex(renderer->acquireCommandBufferLock); + + commandBuffer = D3D11_INTERNAL_GetInactiveCommandBufferFromPool(renderer); + commandBuffer->graphicsPipeline = NULL; + commandBuffer->computePipeline = NULL; + for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) + { + commandBuffer->colorTargetResolveTexture[i] = NULL; + commandBuffer->colorTargetResolveSubresourceIndex[i] = 0; + commandBuffer->colorTargetMsaaHandle[i] = NULL; + } + + for (i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) + { + commandBuffer->vertexUniformBufferNeedsReset[i] = SDL_TRUE; + commandBuffer->fragmentUniformBufferNeedsReset[i] = SDL_TRUE; + commandBuffer->computeUniformBufferNeedsReset[i] = SDL_TRUE; + } + + commandBuffer->needVertexSamplerBind = SDL_TRUE; + commandBuffer->needVertexResourceBind = SDL_TRUE; + commandBuffer->needFragmentSamplerBind = SDL_TRUE; + commandBuffer->needFragmentResourceBind = SDL_TRUE; + commandBuffer->needComputeUAVBind = SDL_TRUE; + commandBuffer->needComputeSRVBind = SDL_TRUE; + + SDL_zeroa(commandBuffer->vertexSamplers); + SDL_zeroa(commandBuffer->vertexShaderResourceViews); + SDL_zeroa(commandBuffer->fragmentSamplers); + SDL_zeroa(commandBuffer->fragmentShaderResourceViews); + SDL_zeroa(commandBuffer->computeShaderResourceViews); + SDL_zeroa(commandBuffer->computeUnorderedAccessViews); + + D3D11_INTERNAL_AcquireFence(commandBuffer); + commandBuffer->autoReleaseFence = 1; + + SDL_UnlockMutex(renderer->acquireCommandBufferLock); + + return (Refresh_CommandBuffer*) commandBuffer; +} + +static void D3D11_INTERNAL_PushUniformData( + D3D11CommandBuffer *d3d11CommandBuffer, + Refresh_ShaderStage shaderStage, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +) { + D3D11Renderer *renderer = d3d11CommandBuffer->renderer; + D3D11UniformBuffer *d3d11UniformBuffer; + ID3D11Buffer *nullBuf = NULL; + Uint32 offsetInConstants, blockSizeInConstants; + Uint32 drawOffset; + + if (shaderStage == REFRESH_SHADERSTAGE_VERTEX) + { + d3d11UniformBuffer = d3d11CommandBuffer->vertexUniformBuffers[slotIndex]; + } + else if (shaderStage == REFRESH_SHADERSTAGE_FRAGMENT) + { + d3d11UniformBuffer = d3d11CommandBuffer->fragmentUniformBuffers[slotIndex]; + } + else if (shaderStage == REFRESH_SHADERSTAGE_COMPUTE) + { + d3d11UniformBuffer = d3d11CommandBuffer->computeUniformBuffers[slotIndex]; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized shader stage!"); + return; + } + + d3d11UniformBuffer->currentBlockSize = + D3D11_INTERNAL_NextHighestAlignment( + dataLengthInBytes, + 256 + ); + + if (d3d11UniformBuffer->offset + d3d11UniformBuffer->currentBlockSize >= d3d11UniformBuffer->bufferContainer->activeBuffer->size) + { + D3D11_INTERNAL_CycleActiveBuffer( + renderer, + d3d11UniformBuffer->bufferContainer + ); + + d3d11UniformBuffer->offset = 0; + + D3D11_INTERNAL_TrackBuffer( + d3d11CommandBuffer, + d3d11UniformBuffer->bufferContainer->activeBuffer + ); + } + + drawOffset = d3d11UniformBuffer->offset; + + D3D11_INTERNAL_SetUniformBufferData( + renderer, + d3d11CommandBuffer, + d3d11UniformBuffer->bufferContainer->activeBuffer, + d3d11UniformBuffer->offset, + data, + dataLengthInBytes + ); + + d3d11UniformBuffer->offset += d3d11UniformBuffer->currentBlockSize; + + offsetInConstants = drawOffset / 16; + blockSizeInConstants = d3d11UniformBuffer->currentBlockSize / 16; + + if (shaderStage == REFRESH_SHADERSTAGE_VERTEX) + { + /* stupid workaround for god awful D3D11 drivers + * see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation + */ + ID3D11DeviceContext1_VSSetConstantBuffers( + d3d11CommandBuffer->context, + slotIndex, + 1, + &nullBuf + ); + + ID3D11DeviceContext1_VSSetConstantBuffers1( + d3d11CommandBuffer->context, + slotIndex, + 1, + &d3d11UniformBuffer->bufferContainer->activeBuffer->handle, + &offsetInConstants, + &blockSizeInConstants + ); + } + else if (shaderStage == REFRESH_SHADERSTAGE_FRAGMENT) + { + /* stupid workaround for god awful D3D11 drivers + * see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation + */ + ID3D11DeviceContext1_PSSetConstantBuffers( + d3d11CommandBuffer->context, + slotIndex, + 1, + &nullBuf + ); + + ID3D11DeviceContext1_PSSetConstantBuffers1( + d3d11CommandBuffer->context, + slotIndex, + 1, + &d3d11UniformBuffer->bufferContainer->activeBuffer->handle, + &offsetInConstants, + &blockSizeInConstants + ); + } + else if (shaderStage == REFRESH_SHADERSTAGE_COMPUTE) + { + /* stupid workaround for god awful D3D11 drivers + * see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation + */ + ID3D11DeviceContext1_CSSetConstantBuffers( + d3d11CommandBuffer->context, + slotIndex, + 1, + &nullBuf + ); + + ID3D11DeviceContext1_CSSetConstantBuffers1( + d3d11CommandBuffer->context, + slotIndex, + 1, + &d3d11UniformBuffer->bufferContainer->activeBuffer->handle, + &offsetInConstants, + &blockSizeInConstants + ); + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized shader stage!"); + } +} + +static void D3D11_BeginRenderPass( + Refresh_CommandBuffer *commandBuffer, + Refresh_ColorAttachmentInfo *colorAttachmentInfos, + Uint32 colorAttachmentCount, + Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer; + ID3D11RenderTargetView* rtvs[MAX_COLOR_TARGET_BINDINGS]; + ID3D11DepthStencilView *dsv = NULL; + Uint32 vpWidth = SDL_MAX_UINT32; + Uint32 vpHeight = SDL_MAX_UINT32; + D3D11_VIEWPORT viewport; + D3D11_RECT scissorRect; + + d3d11CommandBuffer->needVertexSamplerBind = SDL_TRUE; + d3d11CommandBuffer->needVertexResourceBind = SDL_TRUE; + d3d11CommandBuffer->needFragmentSamplerBind = SDL_TRUE; + d3d11CommandBuffer->needFragmentResourceBind = SDL_TRUE; + + /* Clear the bound targets for the current command buffer */ + for (Uint32 i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) + { + d3d11CommandBuffer->colorTargetResolveTexture[i] = NULL; + d3d11CommandBuffer->colorTargetResolveSubresourceIndex[i] = 0; + d3d11CommandBuffer->colorTargetMsaaHandle[i] = NULL; + } + + /* Set up the new color target bindings */ + for (Uint32 i = 0; i < colorAttachmentCount; i += 1) + { + D3D11TextureContainer *container = (D3D11TextureContainer*) colorAttachmentInfos[i].textureSlice.texture; + D3D11TextureSubresource *subresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite( + renderer, + container, + colorAttachmentInfos[i].textureSlice.layer, + colorAttachmentInfos[i].textureSlice.mipLevel, + colorAttachmentInfos[i].cycle + ); + + if (subresource->msaaHandle != NULL) + { + d3d11CommandBuffer->colorTargetResolveTexture[i] = subresource->parent; + d3d11CommandBuffer->colorTargetResolveSubresourceIndex[i] = subresource->index; + d3d11CommandBuffer->colorTargetMsaaHandle[i] = subresource->msaaHandle; + + rtvs[i] = subresource->msaaTargetView; + } + else + { + rtvs[i] = subresource->colorTargetView; + } + + D3D11_INTERNAL_TrackTextureSubresource( + d3d11CommandBuffer, + subresource + ); + } + + /* Get the DSV for the depth stencil attachment, if applicable */ + if (depthStencilAttachmentInfo != NULL) + { + D3D11TextureContainer *container = (D3D11TextureContainer*) depthStencilAttachmentInfo->textureSlice.texture; + D3D11TextureSubresource *subresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite( + renderer, + container, + depthStencilAttachmentInfo->textureSlice.layer, + depthStencilAttachmentInfo->textureSlice.mipLevel, + depthStencilAttachmentInfo->cycle + ); + + dsv = subresource->depthStencilTargetView; + + D3D11_INTERNAL_TrackTextureSubresource( + d3d11CommandBuffer, + subresource + ); + } + + /* Actually set the RTs */ + ID3D11DeviceContext_OMSetRenderTargets( + d3d11CommandBuffer->context, + colorAttachmentCount, + colorAttachmentCount > 0 ? rtvs : NULL, + dsv + ); + + /* Perform load ops on the RTs */ + for (Uint32 i = 0; i < colorAttachmentCount; i += 1) + { + if (colorAttachmentInfos[i].loadOp == REFRESH_LOADOP_CLEAR) + { + float clearColors[] = + { + colorAttachmentInfos[i].clearColor.r, + colorAttachmentInfos[i].clearColor.g, + colorAttachmentInfos[i].clearColor.b, + colorAttachmentInfos[i].clearColor.a + }; + ID3D11DeviceContext_ClearRenderTargetView( + d3d11CommandBuffer->context, + rtvs[i], + clearColors + ); + } + } + + if (depthStencilAttachmentInfo != NULL) + { + D3D11_CLEAR_FLAG dsClearFlags = 0; + if (depthStencilAttachmentInfo->loadOp == REFRESH_LOADOP_CLEAR) + { + dsClearFlags |= D3D11_CLEAR_DEPTH; + } + if (depthStencilAttachmentInfo->stencilLoadOp == REFRESH_LOADOP_CLEAR) + { + dsClearFlags |= D3D11_CLEAR_STENCIL; + } + + if (dsClearFlags != 0) + { + ID3D11DeviceContext_ClearDepthStencilView( + d3d11CommandBuffer->context, + dsv, + dsClearFlags, + depthStencilAttachmentInfo->depthStencilClearValue.depth, + (Uint8) depthStencilAttachmentInfo->depthStencilClearValue.stencil + ); + } + } + + /* The viewport cannot be larger than the smallest attachment. */ + for (Uint32 i = 0; i < colorAttachmentCount; i += 1) + { + D3D11Texture *texture = ((D3D11TextureContainer*) colorAttachmentInfos[i].textureSlice.texture)->activeTexture; + Uint32 w = texture->width >> colorAttachmentInfos[i].textureSlice.mipLevel; + Uint32 h = texture->height >> colorAttachmentInfos[i].textureSlice.mipLevel; + + if (w < vpWidth) + { + vpWidth = w; + } + + if (h < vpHeight) + { + vpHeight = h; + } + } + + if (depthStencilAttachmentInfo != NULL) + { + D3D11Texture *texture = ((D3D11TextureContainer*) depthStencilAttachmentInfo->textureSlice.texture)->activeTexture; + Uint32 w = texture->width >> depthStencilAttachmentInfo->textureSlice.mipLevel; + Uint32 h = texture->height >> depthStencilAttachmentInfo->textureSlice.mipLevel; + + if (w < vpWidth) + { + vpWidth = w; + } + + if (h < vpHeight) + { + vpHeight = h; + } + } + + /* Set default viewport and scissor state */ + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + viewport.Width = (FLOAT) vpWidth; + viewport.Height = (FLOAT) vpHeight; + viewport.MinDepth = 0; + viewport.MaxDepth = 1; + + ID3D11DeviceContext_RSSetViewports( + d3d11CommandBuffer->context, + 1, + &viewport + ); + + scissorRect.left = 0; + scissorRect.right = (LONG) viewport.Width; + scissorRect.top = 0; + scissorRect.bottom = (LONG) viewport.Height; + + ID3D11DeviceContext_RSSetScissorRects( + d3d11CommandBuffer->context, + 1, + &scissorRect + ); +} + +static void D3D11_BindGraphicsPipeline( + Refresh_CommandBuffer *commandBuffer, + Refresh_GraphicsPipeline *graphicsPipeline +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11GraphicsPipeline *pipeline = (D3D11GraphicsPipeline*) graphicsPipeline; + Uint32 offsetInConstants, blockSizeInConstants; + Uint32 i; + + d3d11CommandBuffer->graphicsPipeline = pipeline; + + ID3D11DeviceContext_OMSetBlendState( + d3d11CommandBuffer->context, + pipeline->colorAttachmentBlendState, + pipeline->blendConstants, + pipeline->multisampleState.sampleMask + ); + + ID3D11DeviceContext_OMSetDepthStencilState( + d3d11CommandBuffer->context, + pipeline->depthStencilState, + pipeline->stencilRef + ); + + ID3D11DeviceContext_IASetPrimitiveTopology( + d3d11CommandBuffer->context, + RefreshToD3D11_PrimitiveType[pipeline->primitiveType] + ); + + ID3D11DeviceContext_IASetInputLayout( + d3d11CommandBuffer->context, + pipeline->inputLayout + ); + + ID3D11DeviceContext_RSSetState( + d3d11CommandBuffer->context, + pipeline->rasterizerState + ); + + ID3D11DeviceContext_VSSetShader( + d3d11CommandBuffer->context, + pipeline->vertexShader, + NULL, + 0 + ); + + ID3D11DeviceContext_PSSetShader( + d3d11CommandBuffer->context, + pipeline->fragmentShader, + NULL, + 0 + ); + + for (i = d3d11CommandBuffer->initializedVertexUniformBufferCount; i < pipeline->vertexUniformBufferCount; i += 1) + { + d3d11CommandBuffer->vertexUniformBuffers[i] = D3D11_INTERNAL_CreateUniformBuffer(d3d11CommandBuffer->renderer, UNIFORM_BUFFER_SIZE); + d3d11CommandBuffer->initializedVertexUniformBufferCount += 1; + } + + for (i = d3d11CommandBuffer->initializedFragmentUniformBufferCount; i < pipeline->fragmentUniformBufferCount; i += 1) + { + d3d11CommandBuffer->fragmentUniformBuffers[i] = D3D11_INTERNAL_CreateUniformBuffer(d3d11CommandBuffer->renderer, UNIFORM_BUFFER_SIZE); + d3d11CommandBuffer->initializedFragmentUniformBufferCount += 1; + } + + for (i = 0; i < pipeline->vertexUniformBufferCount; i += 1) + { + /* The first map call on a deferred context must be DISCARD so we reset here */ + if (d3d11CommandBuffer->vertexUniformBufferNeedsReset[i]) + { + D3D11_INTERNAL_CycleActiveBuffer( + d3d11CommandBuffer->renderer, + d3d11CommandBuffer->vertexUniformBuffers[i]->bufferContainer + ); + + d3d11CommandBuffer->vertexUniformBuffers[i]->offset = 0; + d3d11CommandBuffer->vertexUniformBuffers[i]->drawOffset = 0; + + d3d11CommandBuffer->vertexUniformBufferNeedsReset[i] = SDL_FALSE; + } + + /* stupid workaround for god awful D3D11 drivers + * see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation + */ + ID3D11DeviceContext1_VSSetConstantBuffers( + d3d11CommandBuffer->context, + i, + 1, + &d3d11CommandBuffer->vertexUniformBuffers[i]->bufferContainer->activeBuffer->handle + ); + + offsetInConstants = d3d11CommandBuffer->vertexUniformBuffers[i]->drawOffset / 16; + blockSizeInConstants = d3d11CommandBuffer->vertexUniformBuffers[i]->currentBlockSize / 16; + + ID3D11DeviceContext1_VSSetConstantBuffers1( + d3d11CommandBuffer->context, + i, + 1, + &d3d11CommandBuffer->vertexUniformBuffers[i]->bufferContainer->activeBuffer->handle, + &offsetInConstants, + &blockSizeInConstants + ); + + D3D11_INTERNAL_TrackBuffer( + d3d11CommandBuffer, + d3d11CommandBuffer->vertexUniformBuffers[i]->bufferContainer->activeBuffer + ); + } + + for (i = 0; i < pipeline->fragmentUniformBufferCount; i += 1) + { + /* The first map call on a deferred context must be DISCARD so we reset here */ + if (d3d11CommandBuffer->fragmentUniformBufferNeedsReset[i]) + { + D3D11_INTERNAL_CycleActiveBuffer( + d3d11CommandBuffer->renderer, + d3d11CommandBuffer->fragmentUniformBuffers[i]->bufferContainer + ); + + d3d11CommandBuffer->fragmentUniformBuffers[i]->offset = 0; + d3d11CommandBuffer->fragmentUniformBuffers[i]->drawOffset = 0; + + d3d11CommandBuffer->fragmentUniformBufferNeedsReset[i] = SDL_FALSE; + } + + /* stupid workaround for god awful D3D11 drivers + * see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-pssetconstantbuffers1#calling-pssetconstantbuffers1-with-command-list-emulation + */ + ID3D11DeviceContext1_PSSetConstantBuffers( + d3d11CommandBuffer->context, + i, + 1, + &d3d11CommandBuffer->fragmentUniformBuffers[i]->bufferContainer->activeBuffer->handle + ); + + offsetInConstants = d3d11CommandBuffer->fragmentUniformBuffers[i]->drawOffset / 16; + blockSizeInConstants = d3d11CommandBuffer->fragmentUniformBuffers[i]->currentBlockSize / 16; + + ID3D11DeviceContext1_PSSetConstantBuffers1( + d3d11CommandBuffer->context, + i, + 1, + &d3d11CommandBuffer->fragmentUniformBuffers[i]->bufferContainer->activeBuffer->handle, + &offsetInConstants, + &blockSizeInConstants + ); + + D3D11_INTERNAL_TrackBuffer( + d3d11CommandBuffer, + d3d11CommandBuffer->fragmentUniformBuffers[i]->bufferContainer->activeBuffer + ); + } +} + +static void D3D11_SetViewport( + Refresh_CommandBuffer *commandBuffer, + Refresh_Viewport *viewport +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11_VIEWPORT vp = + { + viewport->x, + viewport->y, + viewport->w, + viewport->h, + viewport->minDepth, + viewport->maxDepth + }; + + ID3D11DeviceContext_RSSetViewports( + d3d11CommandBuffer->context, + 1, + &vp + ); +} + +static void D3D11_SetScissor( + Refresh_CommandBuffer *commandBuffer, + Refresh_Rect *scissor +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11_RECT rect = + { + scissor->x, + scissor->y, + scissor->x + scissor->w, + scissor->y + scissor->h + }; + + ID3D11DeviceContext_RSSetScissorRects( + d3d11CommandBuffer->context, + 1, + &rect + ); +} + +static void D3D11_BindVertexBuffers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstBinding, + Refresh_BufferBinding *pBindings, + Uint32 bindingCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + ID3D11Buffer *bufferHandles[MAX_BUFFER_BINDINGS]; + UINT bufferOffsets[MAX_BUFFER_BINDINGS]; + + for (Uint32 i = 0; i < bindingCount; i += 1) + { + D3D11Buffer *currentBuffer = ((D3D11BufferContainer*) pBindings[i].buffer)->activeBuffer; + bufferHandles[i] = currentBuffer->handle; + bufferOffsets[i] = pBindings[i].offset; + D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, currentBuffer); + } + + ID3D11DeviceContext_IASetVertexBuffers( + d3d11CommandBuffer->context, + firstBinding, + bindingCount, + bufferHandles, + &d3d11CommandBuffer->graphicsPipeline->vertexStrides[firstBinding], + bufferOffsets + ); +} + +static void D3D11_BindIndexBuffer( + Refresh_CommandBuffer *commandBuffer, + Refresh_BufferBinding *pBinding, + Refresh_IndexElementSize indexElementSize +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Buffer *d3d11Buffer = ((D3D11BufferContainer*) pBinding->buffer)->activeBuffer; + + D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer); + + ID3D11DeviceContext_IASetIndexBuffer( + d3d11CommandBuffer->context, + d3d11Buffer->handle, + RefreshToD3D11_IndexType[indexElementSize], + (UINT) pBinding->offset + ); +} + +static void D3D11_BindVertexSamplers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSamplerBinding *textureSamplerBindings, + Uint32 bindingCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11TextureContainer *textureContainer; + Uint32 i, j; + + for (i = 0; i < bindingCount; i += 1) + { + textureContainer = (D3D11TextureContainer*) textureSamplerBindings[i].texture; + + for (j = 0; j < textureContainer->activeTexture->subresourceCount; j += 1) + { + D3D11_INTERNAL_TrackTextureSubresource( + d3d11CommandBuffer, + &textureContainer->activeTexture->subresources[j] + ); + } + + d3d11CommandBuffer->vertexSamplers[firstSlot + i] = + ((D3D11Sampler*) textureSamplerBindings[i].sampler)->handle; + + d3d11CommandBuffer->vertexShaderResourceViews[firstSlot + i] = + textureContainer->activeTexture->shaderView; + } + + d3d11CommandBuffer->needVertexSamplerBind = SDL_TRUE; + d3d11CommandBuffer->needVertexResourceBind = SDL_TRUE; +} + +static void D3D11_BindVertexStorageTextures( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11TextureContainer *textureContainer; + D3D11TextureSubresource *textureSubresource; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + textureContainer = (D3D11TextureContainer*) storageTextureSlices[i].texture; + textureSubresource = D3D11_INTERNAL_FetchTextureSubresource( + textureContainer->activeTexture, + storageTextureSlices[i].layer, + storageTextureSlices[i].mipLevel + ); + + D3D11_INTERNAL_TrackTextureSubresource( + d3d11CommandBuffer, + textureSubresource + ); + + d3d11CommandBuffer->vertexShaderResourceViews[ + firstSlot + i + + d3d11CommandBuffer->graphicsPipeline->vertexSamplerCount + ] = textureSubresource->srv; + } + + d3d11CommandBuffer->needVertexResourceBind = SDL_TRUE; +} + +static void D3D11_BindVertexStorageBuffers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11BufferContainer *bufferContainer; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + bufferContainer = (D3D11BufferContainer*) storageBuffers[i]; + + D3D11_INTERNAL_TrackBuffer( + d3d11CommandBuffer, + bufferContainer->activeBuffer + ); + + d3d11CommandBuffer->vertexShaderResourceViews[ + firstSlot + i + + d3d11CommandBuffer->graphicsPipeline->vertexSamplerCount + + d3d11CommandBuffer->graphicsPipeline->vertexStorageTextureCount + ] = bufferContainer->activeBuffer->srv; + } + + d3d11CommandBuffer->needVertexResourceBind = SDL_TRUE; +} + +static void D3D11_BindFragmentSamplers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSamplerBinding *textureSamplerBindings, + Uint32 bindingCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11TextureContainer *textureContainer; + Uint32 i, j; + + for (i = 0; i < bindingCount; i += 1) + { + textureContainer = (D3D11TextureContainer*) textureSamplerBindings[i].texture; + + for (j = 0; j < textureContainer->activeTexture->subresourceCount; j += 1) + { + D3D11_INTERNAL_TrackTextureSubresource( + d3d11CommandBuffer, + &textureContainer->activeTexture->subresources[j] + ); + } + + d3d11CommandBuffer->fragmentSamplers[firstSlot + i] = + ((D3D11Sampler*) textureSamplerBindings[i].sampler)->handle; + + d3d11CommandBuffer->fragmentShaderResourceViews[firstSlot + i] = + textureContainer->activeTexture->shaderView; + } + + d3d11CommandBuffer->needFragmentSamplerBind = SDL_TRUE; + d3d11CommandBuffer->needFragmentResourceBind = SDL_TRUE; +} + +static void D3D11_BindFragmentStorageTextures( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11TextureContainer *textureContainer; + D3D11TextureSubresource *textureSubresource; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + textureContainer = (D3D11TextureContainer*) storageTextureSlices[i].texture; + textureSubresource = D3D11_INTERNAL_FetchTextureSubresource( + textureContainer->activeTexture, + storageTextureSlices[i].layer, + storageTextureSlices[i].mipLevel + ); + + D3D11_INTERNAL_TrackTextureSubresource( + d3d11CommandBuffer, + textureSubresource + ); + + d3d11CommandBuffer->fragmentShaderResourceViews[ + firstSlot + i + + d3d11CommandBuffer->graphicsPipeline->fragmentSamplerCount + ] = textureSubresource->srv; + } + + d3d11CommandBuffer->needFragmentResourceBind = SDL_TRUE; +} + +static void D3D11_BindFragmentStorageBuffers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11BufferContainer *bufferContainer; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + bufferContainer = (D3D11BufferContainer*) storageBuffers[i]; + + D3D11_INTERNAL_TrackBuffer( + d3d11CommandBuffer, + bufferContainer->activeBuffer + ); + + d3d11CommandBuffer->fragmentShaderResourceViews[ + firstSlot + i + + d3d11CommandBuffer->graphicsPipeline->fragmentSamplerCount + + d3d11CommandBuffer->graphicsPipeline->fragmentStorageTextureCount + ] = bufferContainer->activeBuffer->srv; + } + + d3d11CommandBuffer->needFragmentResourceBind = SDL_TRUE; +} + +static void D3D11_INTERNAL_BindGraphicsResources( + D3D11CommandBuffer *commandBuffer +) { + D3D11GraphicsPipeline *graphicsPipeline = commandBuffer->graphicsPipeline; + + Uint32 vertexResourceCount = + graphicsPipeline->vertexSamplerCount + + graphicsPipeline->vertexStorageTextureCount + + graphicsPipeline->vertexStorageBufferCount; + + Uint32 fragmentResourceCount = + graphicsPipeline->fragmentSamplerCount + + graphicsPipeline->fragmentStorageTextureCount + + graphicsPipeline->fragmentStorageBufferCount; + + if (commandBuffer->needVertexSamplerBind) + { + if (graphicsPipeline->vertexSamplerCount > 0) + { + ID3D11DeviceContext_VSSetSamplers( + commandBuffer->context, + 0, + graphicsPipeline->vertexSamplerCount, + commandBuffer->vertexSamplers + ); + } + + commandBuffer->needVertexSamplerBind = SDL_FALSE; + } + + if (commandBuffer->needVertexResourceBind) + { + if (vertexResourceCount > 0) + { + ID3D11DeviceContext_VSSetShaderResources( + commandBuffer->context, + 0, + vertexResourceCount, + commandBuffer->vertexShaderResourceViews + ); + } + + commandBuffer->needVertexResourceBind = SDL_FALSE; + } + + if (commandBuffer->needFragmentSamplerBind) + { + if (graphicsPipeline->fragmentSamplerCount > 0) + { + ID3D11DeviceContext_PSSetSamplers( + commandBuffer->context, + 0, + graphicsPipeline->fragmentSamplerCount, + commandBuffer->fragmentSamplers + ); + } + + commandBuffer->needFragmentSamplerBind = SDL_FALSE; + } + + if (commandBuffer->needFragmentResourceBind) + { + if (fragmentResourceCount > 0) + { + ID3D11DeviceContext_PSSetShaderResources( + commandBuffer->context, + 0, + fragmentResourceCount, + commandBuffer->fragmentShaderResourceViews + ); + } + + commandBuffer->needFragmentResourceBind = SDL_FALSE; + } +} + +static void D3D11_DrawIndexedPrimitives( + Refresh_CommandBuffer *commandBuffer, + Uint32 baseVertex, + Uint32 startIndex, + Uint32 primitiveCount, + Uint32 instanceCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer); + + ID3D11DeviceContext_DrawIndexedInstanced( + d3d11CommandBuffer->context, + PrimitiveVerts(d3d11CommandBuffer->graphicsPipeline->primitiveType, primitiveCount), + instanceCount, + startIndex, + baseVertex, + 0 + ); +} + +static void D3D11_DrawPrimitives( + Refresh_CommandBuffer *commandBuffer, + Uint32 vertexStart, + Uint32 primitiveCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer); + + ID3D11DeviceContext_Draw( + d3d11CommandBuffer->context, + PrimitiveVerts(d3d11CommandBuffer->graphicsPipeline->primitiveType, primitiveCount), + vertexStart + ); +} + +static void D3D11_DrawPrimitivesIndirect( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *buffer, + Uint32 offsetInBytes, + Uint32 drawCount, + Uint32 stride +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer); + + D3D11Buffer *d3d11Buffer = ((D3D11BufferContainer*) buffer)->activeBuffer; + + /* D3D11: "We have multi-draw at home!" + * Multi-draw at home: + */ + for (Uint32 i = 0; i < drawCount; i += 1) + { + ID3D11DeviceContext_DrawInstancedIndirect( + d3d11CommandBuffer->context, + d3d11Buffer->handle, + offsetInBytes + (stride * i) + ); + } + + D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer); +} + +static void D3D11_DrawIndexedPrimitivesIndirect( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *buffer, + Uint32 offsetInBytes, + Uint32 drawCount, + Uint32 stride +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11_INTERNAL_BindGraphicsResources(d3d11CommandBuffer); + + D3D11Buffer *d3d11Buffer = ((D3D11BufferContainer*) buffer)->activeBuffer; + + /* D3D11: "We have multi-draw at home!" + * Multi-draw at home: + */ + for (Uint32 i = 0; i < drawCount; i += 1) + { + ID3D11DeviceContext_DrawIndexedInstancedIndirect( + d3d11CommandBuffer->context, + d3d11Buffer->handle, + offsetInBytes + (stride * i) + ); + } + + D3D11_INTERNAL_TrackBuffer(d3d11CommandBuffer, d3d11Buffer); +} + +static void D3D11_EndRenderPass( + Refresh_CommandBuffer *commandBuffer +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + Uint32 i; + + /* Set render target slots to NULL to avoid NULL set behavior */ + /* https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-pssetshaderresources */ + ID3D11DeviceContext_OMSetRenderTargets( + d3d11CommandBuffer->context, + MAX_COLOR_TARGET_BINDINGS, + nullRTVs, + NULL + ); + + /* Resolve MSAA color render targets */ + for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) + { + if (d3d11CommandBuffer->colorTargetMsaaHandle[i] != NULL) + { + ID3D11DeviceContext_ResolveSubresource( + d3d11CommandBuffer->context, + d3d11CommandBuffer->colorTargetResolveTexture[i]->handle, + d3d11CommandBuffer->colorTargetResolveSubresourceIndex[i], + d3d11CommandBuffer->colorTargetMsaaHandle[i], + 0, + RefreshToD3D11_TextureFormat[d3d11CommandBuffer->colorTargetResolveTexture[i]->format] + ); + } + } +} + +static void D3D11_PushVertexUniformData( + Refresh_CommandBuffer *commandBuffer, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + + if (slotIndex >= d3d11CommandBuffer->graphicsPipeline->vertexUniformBufferCount) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No vertex uniforms exist on slot %i for this pipeline", slotIndex); + return; + } + + D3D11_INTERNAL_PushUniformData( + d3d11CommandBuffer, + REFRESH_SHADERSTAGE_VERTEX, + slotIndex, + data, + dataLengthInBytes + ); +} + +static void D3D11_PushFragmentUniformData( + Refresh_CommandBuffer *commandBuffer, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + + if (slotIndex >= d3d11CommandBuffer->graphicsPipeline->fragmentUniformBufferCount) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No fragment uniforms exist on slot %i for this pipeline", slotIndex); + return; + } + + D3D11_INTERNAL_PushUniformData( + d3d11CommandBuffer, + REFRESH_SHADERSTAGE_FRAGMENT, + slotIndex, + data, + dataLengthInBytes + ); +} + +/* Blit */ + +static void D3D11_Blit( + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *source, + Refresh_TextureRegion *destination, + Refresh_Filter filterMode, + SDL_bool cycle +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer; + D3D11TextureContainer *sourceTextureContainer = (D3D11TextureContainer*) source->textureSlice.texture; + D3D11TextureContainer *destinationTextureContainer = (D3D11TextureContainer*) destination->textureSlice.texture; + Refresh_ColorAttachmentInfo colorAttachmentInfo; + Refresh_Viewport viewport; + Refresh_TextureSamplerBinding textureSamplerBinding; + + if (destinationTextureContainer->activeTexture->depth > 1) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "3D blit destination not implemented!"); + return; + } + + /* Unused */ + colorAttachmentInfo.clearColor.r = 0; + colorAttachmentInfo.clearColor.g = 0; + colorAttachmentInfo.clearColor.b = 0; + colorAttachmentInfo.clearColor.a = 0; + + /* If the entire destination is blitted, we don't have to load */ + if ( + destinationTextureContainer->activeTexture->layerCount == 1 && + destinationTextureContainer->activeTexture->levelCount == 1 && + destination->w == destinationTextureContainer->activeTexture->width && + destination->h == destinationTextureContainer->activeTexture->height && + destination->d == destinationTextureContainer->activeTexture->depth + ) { + colorAttachmentInfo.loadOp = REFRESH_LOADOP_DONT_CARE; + } + else + { + colorAttachmentInfo.loadOp = REFRESH_LOADOP_LOAD; + } + + colorAttachmentInfo.storeOp = REFRESH_STOREOP_STORE; + + colorAttachmentInfo.textureSlice = destination->textureSlice; + colorAttachmentInfo.cycle = cycle; + + D3D11_BeginRenderPass( + commandBuffer, + &colorAttachmentInfo, + 1, + NULL + ); + + viewport.x = (float) destination->x; + viewport.y = (float) destination->y; + viewport.w = (float) destination->w; + viewport.h = (float) destination->h; + viewport.minDepth = 0; + viewport.maxDepth = 1; + + D3D11_SetViewport( + commandBuffer, + &viewport + ); + + if ( + sourceTextureContainer->activeTexture->layerCount == 1 && + sourceTextureContainer->activeTexture->depth == 1 + ) { + /* 2D source */ + D3D11_BindGraphicsPipeline( + commandBuffer, + renderer->blitFrom2DPipeline + ); + } + else if ( + sourceTextureContainer->activeTexture->layerCount > 1 + ) { + /* 2D array source */ + D3D11_BindGraphicsPipeline( + commandBuffer, + renderer->blitFrom2DArrayPipeline + ); + + D3D11_PushFragmentUniformData( + commandBuffer, + 0, + &source->textureSlice.layer, + sizeof(Uint32) + ); + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "3D blit source not implemented!"); + return; + } + + textureSamplerBinding.texture = source->textureSlice.texture; + textureSamplerBinding.sampler = + filterMode == REFRESH_FILTER_NEAREST ? + renderer->blitNearestSampler : + renderer->blitLinearSampler; + + if (((D3D11TextureContainer*) textureSamplerBinding.texture)->activeTexture->shaderView == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Blit source texture must be created with SAMPLER bit!"); + } + + D3D11_BindFragmentSamplers( + commandBuffer, + 0, + &textureSamplerBinding, + 1 + ); + + D3D11_DrawPrimitives( + commandBuffer, + 0, + 1 + ); + + D3D11_EndRenderPass(commandBuffer); +} + +/* Compute State */ + +static void D3D11_BeginComputePass( + Refresh_CommandBuffer *commandBuffer, + Refresh_StorageTextureReadWriteBinding *storageTextureBindings, + Uint32 storageTextureBindingCount, + Refresh_StorageBufferReadWriteBinding *storageBufferBindings, + Uint32 storageBufferBindingCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11TextureContainer *textureContainer; + D3D11TextureSubresource *textureSubresource; + D3D11BufferContainer *bufferContainer; + D3D11Buffer *buffer; + Uint32 i; + + for (i = 0; i < storageTextureBindingCount; i += 1) + { + textureContainer = (D3D11TextureContainer*) storageTextureBindings[i].textureSlice.texture; + + textureSubresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite( + d3d11CommandBuffer->renderer, + textureContainer, + storageTextureBindings[i].textureSlice.layer, + storageTextureBindings[i].textureSlice.mipLevel, + storageTextureBindings[i].cycle + ); + + D3D11_INTERNAL_TrackTextureSubresource( + d3d11CommandBuffer, + textureSubresource + ); + + d3d11CommandBuffer->computeUnorderedAccessViews[i] = + textureSubresource->uav; + } + + for (i = 0; i < storageBufferBindingCount; i += 1) + { + bufferContainer = (D3D11BufferContainer*) storageBufferBindings[i].buffer; + + buffer = D3D11_INTERNAL_PrepareBufferForWrite( + d3d11CommandBuffer->renderer, + bufferContainer, + storageBufferBindings[i].cycle + ); + + D3D11_INTERNAL_TrackBuffer( + d3d11CommandBuffer, + buffer + ); + + d3d11CommandBuffer->computeUnorderedAccessViews[ + i + storageTextureBindingCount + ] = buffer->uav; + } + + d3d11CommandBuffer->needComputeUAVBind = SDL_TRUE; +} + +static void D3D11_BindComputePipeline( + Refresh_CommandBuffer *commandBuffer, + Refresh_ComputePipeline *computePipeline +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11ComputePipeline *pipeline = (D3D11ComputePipeline*) computePipeline; + Uint32 offsetInConstants, blockSizeInConstants; + Uint32 i; + + d3d11CommandBuffer->computePipeline = pipeline; + + ID3D11DeviceContext_CSSetShader( + d3d11CommandBuffer->context, + pipeline->computeShader, + NULL, + 0 + ); + + for (i = d3d11CommandBuffer->initializedComputeUniformBufferCount; i < pipeline->uniformBufferCount; i += 1) + { + d3d11CommandBuffer->computeUniformBuffers[i] = D3D11_INTERNAL_CreateUniformBuffer(d3d11CommandBuffer->renderer, UNIFORM_BUFFER_SIZE); + d3d11CommandBuffer->initializedComputeUniformBufferCount += 1; + } + + for (i = 0; i < pipeline->uniformBufferCount; i += 1) + { + /* The first map call on a deferred context must be DISCARD so we reset here */ + if (d3d11CommandBuffer->computeUniformBufferNeedsReset[i]) + { + D3D11_INTERNAL_CycleActiveBuffer( + d3d11CommandBuffer->renderer, + d3d11CommandBuffer->computeUniformBuffers[i]->bufferContainer + ); + + d3d11CommandBuffer->computeUniformBuffers[i]->offset = 0; + d3d11CommandBuffer->computeUniformBuffers[i]->drawOffset = 0; + + d3d11CommandBuffer->computeUniformBufferNeedsReset[i] = SDL_FALSE; + } + + /* stupid workaround for god awful D3D11 drivers + * see: https://learn.microsoft.com/en-us/windows/win32/api/d3d11_1/nf-d3d11_1-id3d11devicecontext1-vssetconstantbuffers1#calling-vssetconstantbuffers1-with-command-list-emulation + */ + ID3D11DeviceContext1_CSSetConstantBuffers( + d3d11CommandBuffer->context, + i, + 1, + &d3d11CommandBuffer->computeUniformBuffers[i]->bufferContainer->activeBuffer->handle + ); + + offsetInConstants = d3d11CommandBuffer->computeUniformBuffers[i]->drawOffset / 16; + blockSizeInConstants = d3d11CommandBuffer->computeUniformBuffers[i]->currentBlockSize / 16; + + ID3D11DeviceContext1_CSSetConstantBuffers1( + d3d11CommandBuffer->context, + i, + 1, + &d3d11CommandBuffer->computeUniformBuffers[i]->bufferContainer->activeBuffer->handle, + &offsetInConstants, + &blockSizeInConstants + ); + + D3D11_INTERNAL_TrackBuffer( + d3d11CommandBuffer, + d3d11CommandBuffer->computeUniformBuffers[i]->bufferContainer->activeBuffer + ); + } +} + +static void D3D11_BindComputeStorageTextures( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11TextureContainer *textureContainer; + D3D11TextureSubresource *textureSubresource; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + textureContainer = (D3D11TextureContainer*) storageTextureSlices[i].texture; + textureSubresource = D3D11_INTERNAL_FetchTextureSubresource( + textureContainer->activeTexture, + storageTextureSlices[i].layer, + storageTextureSlices[i].mipLevel + ); + + D3D11_INTERNAL_TrackTextureSubresource( + d3d11CommandBuffer, + textureSubresource + ); + + d3d11CommandBuffer->computeShaderResourceViews[firstSlot + i] = + textureSubresource->srv; + } + + d3d11CommandBuffer->needComputeSRVBind = SDL_TRUE; +} + +static void D3D11_BindComputeStorageBuffers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11BufferContainer *bufferContainer; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + bufferContainer = (D3D11BufferContainer*) storageBuffers[i]; + + D3D11_INTERNAL_TrackBuffer( + d3d11CommandBuffer, + bufferContainer->activeBuffer + ); + + d3d11CommandBuffer->computeShaderResourceViews[ + firstSlot + i + + d3d11CommandBuffer->computePipeline->readOnlyStorageTextureCount + ] = bufferContainer->activeBuffer->srv; + } + + d3d11CommandBuffer->needComputeSRVBind = SDL_TRUE; +} + +static void D3D11_PushComputeUniformData( + Refresh_CommandBuffer *commandBuffer, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + + if (slotIndex >= d3d11CommandBuffer->computePipeline->uniformBufferCount) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No compute uniforms exist on slot %i for this pipeline", slotIndex); + return; + } + + D3D11_INTERNAL_PushUniformData( + d3d11CommandBuffer, + REFRESH_SHADERSTAGE_COMPUTE, + slotIndex, + data, + dataLengthInBytes + ); +} + +static void D3D11_INTERNAL_BindComputeResources( + D3D11CommandBuffer *commandBuffer +) { + D3D11ComputePipeline *computePipeline = commandBuffer->computePipeline; + + Uint32 readOnlyResourceCount = + computePipeline->readOnlyStorageTextureCount + + computePipeline->readOnlyStorageBufferCount; + + Uint32 readWriteResourceCount = + computePipeline->readWriteStorageTextureCount + + computePipeline->readWriteStorageBufferCount; + + if (commandBuffer->needComputeUAVBind) + { + ID3D11DeviceContext_CSSetUnorderedAccessViews( + commandBuffer->context, + 0, + readWriteResourceCount, + commandBuffer->computeUnorderedAccessViews, + NULL + ); + + commandBuffer->needComputeUAVBind = SDL_FALSE; + } + + if (commandBuffer->needComputeSRVBind) + { + ID3D11DeviceContext_CSSetShaderResources( + commandBuffer->context, + 0, + readOnlyResourceCount, + commandBuffer->computeShaderResourceViews + ); + + commandBuffer->needComputeSRVBind = SDL_FALSE; + } +} + +static void D3D11_DispatchCompute( + Refresh_CommandBuffer *commandBuffer, + Uint32 groupCountX, + Uint32 groupCountY, + Uint32 groupCountZ +) { + D3D11CommandBuffer* d3d11CommandBuffer = (D3D11CommandBuffer*)commandBuffer; + D3D11_INTERNAL_BindComputeResources(d3d11CommandBuffer); + + ID3D11DeviceContext_Dispatch( + d3d11CommandBuffer->context, + groupCountX, + groupCountY, + groupCountZ + ); +} + +static void D3D11_EndComputePass( + Refresh_CommandBuffer *commandBuffer +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + + /* reset UAV slots to avoid NULL set behavior */ + /* https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-cssetshaderresources */ + ID3D11DeviceContext_CSSetUnorderedAccessViews( + d3d11CommandBuffer->context, + 0, + MAX_STORAGE_TEXTURES_PER_STAGE + MAX_STORAGE_BUFFERS_PER_STAGE, + nullUAVs, + NULL + ); + + d3d11CommandBuffer->computePipeline = NULL; +} + +/* Fence Cleanup */ + +static void D3D11_INTERNAL_ReleaseFenceToPool( + D3D11Renderer *renderer, + D3D11Fence *fence +) { + SDL_LockMutex(renderer->fenceLock); + + if (renderer->availableFenceCount == renderer->availableFenceCapacity) + { + renderer->availableFenceCapacity *= 2; + renderer->availableFences = SDL_realloc( + renderer->availableFences, + renderer->availableFenceCapacity * sizeof(D3D11Fence*) + ); + } + renderer->availableFences[renderer->availableFenceCount] = fence; + renderer->availableFenceCount += 1; + + SDL_UnlockMutex(renderer->fenceLock); +} + +static void D3D11_ReleaseFence( + Refresh_Renderer *driverData, + Refresh_Fence *fence +) { + D3D11Fence *d3d11Fence = (D3D11Fence*) fence; + + if (SDL_AtomicDecRef(&d3d11Fence->referenceCount)) + { + D3D11_INTERNAL_ReleaseFenceToPool( + (D3D11Renderer*) driverData, + d3d11Fence + ); + } +} + +/* Cleanup */ + +static void D3D11_INTERNAL_CleanCommandBuffer( + D3D11Renderer *renderer, + D3D11CommandBuffer *commandBuffer +) { + /* Reference Counting */ + + for (Uint32 i = 0; i < commandBuffer->usedBufferCount; i += 1) + { + (void)SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->referenceCount); + } + commandBuffer->usedBufferCount = 0; + + for (Uint32 i = 0; i < commandBuffer->usedTransferBufferCount; i += 1) + { + (void)SDL_AtomicDecRef(&commandBuffer->usedTransferBuffers[i]->referenceCount); + } + commandBuffer->usedTransferBufferCount = 0; + + for (Uint32 i = 0; i < commandBuffer->usedTextureSubresourceCount; i += 1) + { + (void)SDL_AtomicDecRef(&commandBuffer->usedTextureSubresources[i]->referenceCount); + } + commandBuffer->usedTextureSubresourceCount = 0; + + /* Reset presentation */ + commandBuffer->windowDataCount = 0; + + /* The fence is now available (unless SubmitAndAcquireFence was called) */ + if (commandBuffer->autoReleaseFence) + { + D3D11_ReleaseFence( + (Refresh_Renderer*) renderer, + (Refresh_Fence*) commandBuffer->fence + ); + } + + /* Return command buffer to pool */ + SDL_LockMutex(renderer->acquireCommandBufferLock); + if (renderer->availableCommandBufferCount == renderer->availableCommandBufferCapacity) + { + renderer->availableCommandBufferCapacity += 1; + renderer->availableCommandBuffers = SDL_realloc( + renderer->availableCommandBuffers, + renderer->availableCommandBufferCapacity * sizeof(D3D11CommandBuffer*) + ); + } + renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer; + renderer->availableCommandBufferCount += 1; + SDL_UnlockMutex(renderer->acquireCommandBufferLock); + + /* Remove this command buffer from the submitted list */ + for (Uint32 i = 0; i < renderer->submittedCommandBufferCount; i += 1) + { + if (renderer->submittedCommandBuffers[i] == commandBuffer) + { + renderer->submittedCommandBuffers[i] = renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount - 1]; + renderer->submittedCommandBufferCount -= 1; + } + } +} + +static void D3D11_INTERNAL_PerformPendingDestroys( + D3D11Renderer *renderer +) { + Sint32 referenceCount = 0; + Sint32 i; + Uint32 j, k; + + for (i = renderer->transferBufferContainersToDestroyCount - 1; i >= 0; i -= 1) + { + referenceCount = 0; + for (j = 0; j < renderer->transferBufferContainersToDestroy[i]->bufferCount; j += 1) + { + referenceCount += SDL_AtomicGet(&renderer->transferBufferContainersToDestroy[i]->buffers[j]->referenceCount); + } + + if (referenceCount == 0) + { + D3D11_INTERNAL_DestroyTransferBufferContainer( + renderer->transferBufferContainersToDestroy[i] + ); + + renderer->transferBufferContainersToDestroy[i] = renderer->transferBufferContainersToDestroy[renderer->transferBufferContainersToDestroyCount - 1]; + renderer->transferBufferContainersToDestroyCount -= 1; + } + } + + for (i = renderer->bufferContainersToDestroyCount - 1; i >= 0; i -= 1) + { + referenceCount = 0; + for (j = 0; j < renderer->bufferContainersToDestroy[i]->bufferCount; j += 1) + { + referenceCount += SDL_AtomicGet(&renderer->bufferContainersToDestroy[i]->buffers[j]->referenceCount); + } + + if (referenceCount == 0) + { + D3D11_INTERNAL_DestroyBufferContainer( + renderer->bufferContainersToDestroy[i] + ); + + renderer->bufferContainersToDestroy[i] = renderer->bufferContainersToDestroy[renderer->bufferContainersToDestroyCount - 1]; + renderer->bufferContainersToDestroyCount -= 1; + } + } + + for (i = renderer->textureContainersToDestroyCount - 1; i >= 0; i -= 1) + { + referenceCount = 0; + for (j = 0; j < renderer->textureContainersToDestroy[i]->textureCount; j += 1) + { + for (k = 0; k < renderer->textureContainersToDestroy[i]->textures[j]->subresourceCount; k += 1) + { + referenceCount += SDL_AtomicGet(&renderer->textureContainersToDestroy[i]->textures[j]->subresources[k].referenceCount); + } + } + + if (referenceCount == 0) + { + D3D11_INTERNAL_DestroyTextureContainer( + renderer->textureContainersToDestroy[i] + ); + + renderer->textureContainersToDestroy[i] = renderer->textureContainersToDestroy[renderer->textureContainersToDestroyCount - 1]; + renderer->textureContainersToDestroyCount -= 1; + } + } +} + +/* Fences */ + +static void D3D11_INTERNAL_WaitForFence( + D3D11Renderer *renderer, + D3D11Fence *fence +) { + BOOL queryData; + HRESULT res; + + SDL_LockMutex(renderer->contextLock); + + do + { + res = ID3D11DeviceContext_GetData( + renderer->immediateContext, + (ID3D11Asynchronous*)fence->handle, + &queryData, + sizeof(queryData), + 0 + ); + } + while (res != S_OK); /* Spin until we get a result back... */ + + SDL_UnlockMutex(renderer->contextLock); +} + +static void D3D11_WaitForFences( + Refresh_Renderer *driverData, + SDL_bool waitAll, + Uint32 fenceCount, + Refresh_Fence **pFences +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11Fence *fence; + BOOL queryData; + HRESULT res = S_FALSE; + + if (waitAll) + { + for (Uint32 i = 0; i < fenceCount; i += 1) + { + fence = (D3D11Fence*) pFences[i]; + D3D11_INTERNAL_WaitForFence(renderer, fence); + } + } + else + { + SDL_LockMutex(renderer->contextLock); + + while (res != S_OK) + { + for (Uint32 i = 0; i < fenceCount; i += 1) + { + fence = (D3D11Fence*) pFences[i]; + res = ID3D11DeviceContext_GetData( + renderer->immediateContext, + (ID3D11Asynchronous*) fence->handle, + &queryData, + sizeof(queryData), + 0 + ); + if (res == S_OK) + { + break; + } + } + } + + SDL_UnlockMutex(renderer->contextLock); + } + + SDL_LockMutex(renderer->contextLock); + + /* Check if we can perform any cleanups */ + for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) + { + res = ID3D11DeviceContext_GetData( + renderer->immediateContext, + (ID3D11Asynchronous*) renderer->submittedCommandBuffers[i]->fence->handle, + &queryData, + sizeof(queryData), + 0 + ); + if (res == S_OK) + { + D3D11_INTERNAL_CleanCommandBuffer( + renderer, + renderer->submittedCommandBuffers[i] + ); + } + } + + D3D11_INTERNAL_PerformPendingDestroys(renderer); + + SDL_UnlockMutex(renderer->contextLock); + +} + +static SDL_bool D3D11_QueryFence( + Refresh_Renderer *driverData, + Refresh_Fence *fence +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11Fence *d3d11Fence = (D3D11Fence*) fence; + BOOL queryData; + HRESULT res; + + SDL_LockMutex(renderer->contextLock); + + res = ID3D11DeviceContext_GetData( + renderer->immediateContext, + (ID3D11Asynchronous*) d3d11Fence->handle, + &queryData, + sizeof(queryData), + 0 + ); + + SDL_UnlockMutex(renderer->contextLock); + + return res == S_OK; +} + +/* Window and Swapchain Management */ + +static D3D11WindowData* D3D11_INTERNAL_FetchWindowData( + SDL_Window *window +) { + return (D3D11WindowData*) SDL_GetWindowData(window, WINDOW_PROPERTY_DATA); +} + +static Uint8 D3D11_INTERNAL_InitializeSwapchainTexture( + D3D11Renderer *renderer, + IDXGISwapChain *swapchain, + DXGI_FORMAT swapchainFormat, + DXGI_FORMAT rtvFormat, + D3D11Texture *pTexture +) { + ID3D11Texture2D *swapchainTexture; + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc; + D3D11_TEXTURE2D_DESC textureDesc; + ID3D11ShaderResourceView *srv; + ID3D11RenderTargetView *rtv; + ID3D11UnorderedAccessView *uav; + HRESULT res; + + /* Clear all the texture data */ + SDL_zerop(pTexture); + + /* Grab the buffer from the swapchain */ + res = IDXGISwapChain_GetBuffer( + swapchain, + 0, + &D3D_IID_ID3D11Texture2D, + (void**) &swapchainTexture + ); + ERROR_CHECK_RETURN("Could not get buffer from swapchain!", 0); + + /* Create the SRV for the swapchain */ + srvDesc.Format = swapchainFormat; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = 1; + srvDesc.Texture2D.MostDetailedMip = 0; + + res = ID3D11Device_CreateShaderResourceView( + renderer->device, + (ID3D11Resource *)swapchainTexture, + &srvDesc, + &srv + ); + if (FAILED(res)) + { + ID3D11Texture2D_Release(swapchainTexture); + D3D11_INTERNAL_LogError(renderer->device, "Swapchain SRV creation failed", res); + return 0; + } + + /* Create the RTV for the swapchain */ + rtvDesc.Format = rtvFormat; + rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtvDesc.Texture2D.MipSlice = 0; + + res = ID3D11Device_CreateRenderTargetView( + renderer->device, + (ID3D11Resource*) swapchainTexture, + &rtvDesc, + &rtv + ); + if (FAILED(res)) + { + ID3D11ShaderResourceView_Release(srv); + ID3D11Texture2D_Release(swapchainTexture); + D3D11_INTERNAL_LogError(renderer->device, "Swapchain RTV creation failed", res); + return 0; + } + + uavDesc.Format = swapchainFormat; + uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; + uavDesc.Texture2D.MipSlice = 0; + + res = ID3D11Device_CreateUnorderedAccessView( + renderer->device, + (ID3D11Resource *)swapchainTexture, + &uavDesc, + &uav + ); + if (FAILED(res)) + { + ID3D11ShaderResourceView_Release(srv); + ID3D11RenderTargetView_Release(rtv); + ID3D11Texture2D_Release(swapchainTexture); + D3D11_INTERNAL_LogError(renderer->device, "Swapchain UAV creation failed", res); + return 0; + } + + /* Fill out the texture struct */ + pTexture->handle = NULL; /* This will be set in AcquireSwapchainTexture. */ + pTexture->shaderView = srv; + pTexture->subresourceCount = 1; + pTexture->subresources = SDL_malloc(sizeof(D3D11TextureSubresource)); + pTexture->subresources[0].colorTargetView = rtv; + pTexture->subresources[0].srv = srv; + pTexture->subresources[0].uav = uav; + pTexture->subresources[0].depthStencilTargetView = NULL; + pTexture->subresources[0].msaaHandle = NULL; + pTexture->subresources[0].msaaTargetView = NULL; + pTexture->subresources[0].layer = 0; + pTexture->subresources[0].level = 0; + pTexture->subresources[0].index = 0; + pTexture->subresources[0].parent = pTexture; + SDL_AtomicSet(&pTexture->subresources[0].referenceCount, 0); + + ID3D11Texture2D_GetDesc(swapchainTexture, &textureDesc); + pTexture->levelCount = textureDesc.MipLevels; + pTexture->width = textureDesc.Width; + pTexture->height = textureDesc.Height; + pTexture->depth = 1; + pTexture->isCube = 0; + pTexture->isRenderTarget = 1; + + /* Cleanup */ + ID3D11Texture2D_Release(swapchainTexture); + + return 1; +} + +static Uint8 D3D11_INTERNAL_CreateSwapchain( + D3D11Renderer *renderer, + D3D11WindowData *windowData, + Refresh_SwapchainComposition swapchainComposition, + Refresh_PresentMode presentMode +) { + SDL_SysWMinfo info; + HWND dxgiHandle; + int width, height; + Uint32 i; + DXGI_SWAP_CHAIN_DESC swapchainDesc; + DXGI_FORMAT swapchainFormat; + IDXGIFactory1 *pParent; + IDXGISwapChain *swapchain; + IDXGISwapChain3 *swapchain3; + Uint32 colorSpaceSupport; + HRESULT res; + + /* Get the DXGI handle */ +#ifdef _WIN32 + SDL_VERSION(&info.version); + SDL_GetWindowWMInfo((SDL_Window*) windowData->window, &info); + dxgiHandle = info.info.win.window; +#else + dxgiHandle = (HWND) windowData->window; +#endif + + /* Get the window size */ + SDL_GetWindowSize(windowData->window, &width, &height); + + swapchainFormat = SwapchainCompositionToTextureFormat[swapchainComposition]; + + /* Initialize the swapchain buffer descriptor */ + swapchainDesc.BufferDesc.Width = 0; + swapchainDesc.BufferDesc.Height = 0; + swapchainDesc.BufferDesc.RefreshRate.Numerator = 0; + swapchainDesc.BufferDesc.RefreshRate.Denominator = 0; + swapchainDesc.BufferDesc.Format = swapchainFormat; + swapchainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + swapchainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + + /* Initialize the rest of the swapchain descriptor */ + swapchainDesc.SampleDesc.Count = 1; + swapchainDesc.SampleDesc.Quality = 0; + swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_UNORDERED_ACCESS | DXGI_USAGE_SHADER_INPUT; + swapchainDesc.BufferCount = 2; + swapchainDesc.OutputWindow = dxgiHandle; + swapchainDesc.Windowed = 1; + + if (renderer->supportsTearing) + { + swapchainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; + /* We know this is supported because tearing support implies DXGI 1.5+ */ + swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + } + else + { + swapchainDesc.Flags = 0; + swapchainDesc.SwapEffect = ( + renderer->supportsFlipDiscard ? + DXGI_SWAP_EFFECT_FLIP_DISCARD : + DXGI_SWAP_EFFECT_DISCARD + ); + } + + /* Create the swapchain! */ + res = IDXGIFactory1_CreateSwapChain( + (IDXGIFactory1*) renderer->factory, + (IUnknown*) renderer->device, + &swapchainDesc, + &swapchain + ); + ERROR_CHECK_RETURN("Could not create swapchain", 0); + + /* + * The swapchain's parent is a separate factory from the factory that + * we used to create the swapchain, and only that parent can be used to + * set the window association. Trying to set an association on our factory + * will silently fail and doesn't even verify arguments or return errors. + * See https://gamedev.net/forums/topic/634235-dxgidisabling-altenter/4999955/ + */ + res = IDXGISwapChain_GetParent( + swapchain, + &D3D_IID_IDXGIFactory1, + (void**) &pParent + ); + if (FAILED(res)) + { + SDL_LogWarn( + SDL_LOG_CATEGORY_APPLICATION, + "Could not get swapchain parent! Error Code: " HRESULT_FMT, + res + ); + } + else + { + /* Disable DXGI window crap */ + res = IDXGIFactory1_MakeWindowAssociation( + pParent, + dxgiHandle, + DXGI_MWA_NO_WINDOW_CHANGES + ); + if (FAILED(res)) + { + SDL_LogWarn( + SDL_LOG_CATEGORY_APPLICATION, + "MakeWindowAssociation failed! Error Code: " HRESULT_FMT, + res + ); + } + + /* We're done with the parent now */ + IDXGIFactory1_Release(pParent); + } + /* Initialize the swapchain data */ + windowData->swapchain = swapchain; + windowData->presentMode = presentMode; + windowData->swapchainComposition = swapchainComposition; + windowData->swapchainFormat = swapchainFormat; + windowData->swapchainColorSpace = SwapchainCompositionToColorSpace[swapchainComposition]; + windowData->frameCounter = 0; + + for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) + { + windowData->inFlightFences[i] = NULL; + } + + if (SUCCEEDED(IDXGISwapChain3_QueryInterface( + swapchain, + &D3D_IID_IDXGISwapChain3, + (void**) &swapchain3 + ))) { + IDXGISwapChain3_CheckColorSpaceSupport( + swapchain3, + windowData->swapchainColorSpace, + &colorSpaceSupport + ); + + if (!(colorSpaceSupport & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Requested colorspace is unsupported!"); + return 0; + } + + IDXGISwapChain3_SetColorSpace1( + swapchain3, + windowData->swapchainColorSpace + ); + + IDXGISwapChain3_Release(swapchain3); + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "DXGI 1.4 not supported, cannot use colorspace other than REFRESH_COLORSPACE_NONLINEAR_SRGB!"); + return 0; + } + + /* If a you are using a FLIP model format you can't create the swapchain as DXGI_FORMAT_B8G8R8A8_UNORM_SRGB. + * You have to create the swapchain as DXGI_FORMAT_B8G8R8A8_UNORM and then set the render target view's format to DXGI_FORMAT_B8G8R8A8_UNORM_SRGB + */ + if (!D3D11_INTERNAL_InitializeSwapchainTexture( + renderer, + swapchain, + swapchainFormat, + (swapchainComposition == REFRESH_SWAPCHAINCOMPOSITION_SDR_LINEAR) ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : windowData->swapchainFormat, + &windowData->texture + )) { + IDXGISwapChain_Release(swapchain); + return 0; + } + + /* Initialize dummy container */ + SDL_zerop(&windowData->textureContainer); + windowData->textureContainer.textures = SDL_calloc(1, sizeof(D3D11Texture*)); + + return 1; +} + +static Uint8 D3D11_INTERNAL_ResizeSwapchain( + D3D11Renderer *renderer, + D3D11WindowData *windowData, + Sint32 width, + Sint32 height +) { + /* Release the old views */ + ID3D11ShaderResourceView_Release(windowData->texture.shaderView); + ID3D11RenderTargetView_Release(windowData->texture.subresources[0].colorTargetView); + /* NOTE: subresource srv is same as shaderView, not released */ + ID3D11UnorderedAccessView_Release(windowData->texture.subresources[0].uav); + SDL_free(windowData->texture.subresources); + + /* Resize the swapchain */ + HRESULT res = IDXGISwapChain_ResizeBuffers( + windowData->swapchain, + 0, /* Keep buffer count the same */ + width, + height, + DXGI_FORMAT_UNKNOWN, /* Keep the old format */ + renderer->supportsTearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0 + ); + ERROR_CHECK_RETURN("Could not resize swapchain buffers", 0); + + /* Create the texture object for the swapchain */ + return D3D11_INTERNAL_InitializeSwapchainTexture( + renderer, + windowData->swapchain, + windowData->swapchainFormat, + (windowData->swapchainComposition == REFRESH_SWAPCHAINCOMPOSITION_SDR_LINEAR) ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : windowData->swapchainFormat, + &windowData->texture + ); +} + +static SDL_bool D3D11_SupportsSwapchainComposition( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + DXGI_FORMAT format; + Uint32 formatSupport = 0; + HRESULT res; + + format = SwapchainCompositionToTextureFormat[swapchainComposition]; + + res = ID3D11Device_CheckFormatSupport( + renderer->device, + format, + &formatSupport + ); + if (FAILED(res)) + { + /* Format is apparently unknown */ + return SDL_FALSE; + } + + return (formatSupport & D3D11_FORMAT_SUPPORT_DISPLAY); +} + +static SDL_bool D3D11_SupportsPresentMode( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_PresentMode presentMode +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + (void)window; /* used by other backends */ + switch (presentMode) + { + case REFRESH_PRESENTMODE_IMMEDIATE: + case REFRESH_PRESENTMODE_VSYNC: + return SDL_TRUE; + case REFRESH_PRESENTMODE_MAILBOX: + return renderer->supportsFlipDiscard; + } + SDL_assert(!"Unrecognized present mode"); + return SDL_FALSE; +} + +static SDL_bool D3D11_ClaimWindow( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition, + Refresh_PresentMode presentMode +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window); + + if (windowData == NULL) + { + windowData = (D3D11WindowData*) SDL_malloc(sizeof(D3D11WindowData)); + windowData->window = window; + + if (D3D11_INTERNAL_CreateSwapchain(renderer, windowData, swapchainComposition, presentMode)) + { + SDL_SetWindowData(window, WINDOW_PROPERTY_DATA, windowData); + + SDL_LockMutex(renderer->windowLock); + + if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) + { + renderer->claimedWindowCapacity *= 2; + renderer->claimedWindows = SDL_realloc( + renderer->claimedWindows, + renderer->claimedWindowCapacity * sizeof(D3D11WindowData*) + ); + } + renderer->claimedWindows[renderer->claimedWindowCount] = windowData; + renderer->claimedWindowCount += 1; + + SDL_UnlockMutex(renderer->windowLock); + + return 1; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not create swapchain, failed to claim window!"); + SDL_free(windowData); + return 0; + } + } + else + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Window already claimed!"); + return 0; + } +} + +static void D3D11_INTERNAL_DestroySwapchain( + D3D11Renderer *renderer, + D3D11WindowData *windowData +) { + Uint32 i; + + D3D11_Wait((Refresh_Renderer*) renderer); + + ID3D11ShaderResourceView_Release(windowData->texture.shaderView); + ID3D11RenderTargetView_Release(windowData->texture.subresources[0].colorTargetView); + /* NOTE: subresource srv is same as shaderView, not released */ + ID3D11UnorderedAccessView_Release(windowData->texture.subresources[0].uav); + SDL_free(windowData->texture.subresources); + SDL_free(windowData->textureContainer.textures); + IDXGISwapChain_Release(windowData->swapchain); + + /* DXGI will crash if we don't flush deferred swapchain destruction */ + SDL_LockMutex(renderer->contextLock); + ID3D11DeviceContext_ClearState(renderer->immediateContext); + ID3D11DeviceContext_Flush(renderer->immediateContext); + SDL_UnlockMutex(renderer->contextLock); + + windowData->swapchain = NULL; + + for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) + { + if (windowData->inFlightFences[i] != NULL) + { + D3D11_ReleaseFence( + (Refresh_Renderer*) renderer, + (Refresh_Fence*) windowData->inFlightFences[i] + ); + } + } +} + +static void D3D11_UnclaimWindow( + Refresh_Renderer *driverData, + SDL_Window *window +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window); + + if (windowData == NULL) + { + return; + } + + D3D11_INTERNAL_DestroySwapchain( + renderer, + windowData + ); + + SDL_LockMutex(renderer->windowLock); + for (Uint32 i = 0; i < renderer->claimedWindowCount; i += 1) + { + if (renderer->claimedWindows[i]->window == window) + { + renderer->claimedWindows[i] = renderer->claimedWindows[renderer->claimedWindowCount - 1]; + renderer->claimedWindowCount -= 1; + break; + } + } + SDL_UnlockMutex(renderer->windowLock); + + SDL_free(windowData); + SDL_SetWindowData(window, WINDOW_PROPERTY_DATA, NULL); +} + +static Refresh_Texture* D3D11_AcquireSwapchainTexture( + Refresh_CommandBuffer *commandBuffer, + SDL_Window *window, + Uint32 *pWidth, + Uint32 *pHeight +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer; + D3D11WindowData *windowData; + DXGI_SWAP_CHAIN_DESC swapchainDesc; + int w, h; + HRESULT res; + + windowData = D3D11_INTERNAL_FetchWindowData(window); + if (windowData == NULL) + { + return NULL; + } + + /* Check for window size changes and resize the swapchain if needed. */ + IDXGISwapChain_GetDesc(windowData->swapchain, &swapchainDesc); + SDL_GetWindowSize(window, &w, &h); + + if (w != swapchainDesc.BufferDesc.Width || h != swapchainDesc.BufferDesc.Height) + { + res = D3D11_INTERNAL_ResizeSwapchain( + renderer, + windowData, + w, + h + ); + ERROR_CHECK_RETURN("Could not resize swapchain", NULL); + } + + if (windowData->inFlightFences[windowData->frameCounter] != NULL) + { + if (windowData->presentMode == REFRESH_PRESENTMODE_VSYNC) + { + /* In VSYNC mode, block until the least recent presented frame is done */ + D3D11_WaitForFences( + (Refresh_Renderer*) renderer, + SDL_TRUE, + 1, + (Refresh_Fence**) &windowData->inFlightFences[windowData->frameCounter] + ); + } + else + { + if (!D3D11_QueryFence( + (Refresh_Renderer*) d3d11CommandBuffer->renderer, + (Refresh_Fence*) windowData->inFlightFences[windowData->frameCounter] + )) { + /* + * In MAILBOX or IMMEDIATE mode, if the least recent fence is not signaled, + * return NULL to indicate that rendering should be skipped + */ + return NULL; + } + } + + D3D11_ReleaseFence( + (Refresh_Renderer*) d3d11CommandBuffer->renderer, + (Refresh_Fence*) windowData->inFlightFences[windowData->frameCounter] + ); + + windowData->inFlightFences[windowData->frameCounter] = NULL; + } + + /* Set the handle on the windowData texture data. */ + res = IDXGISwapChain_GetBuffer( + windowData->swapchain, + 0, + &D3D_IID_ID3D11Texture2D, + (void**) &windowData->texture.handle + ); + ERROR_CHECK_RETURN("Could not acquire swapchain!", NULL); + + /* Send the dimensions to the out parameters. */ + *pWidth = windowData->texture.width; + *pHeight = windowData->texture.height; + + /* Set up the texture container */ + windowData->textureContainer.canBeCycled = 0; + windowData->textureContainer.activeTexture = &windowData->texture; + windowData->textureContainer.textures[0] = &windowData->texture; + windowData->textureContainer.textureCapacity = 1; + windowData->textureContainer.textureCount = 1; + + /* Set up presentation */ + if (d3d11CommandBuffer->windowDataCount == d3d11CommandBuffer->windowDataCapacity) + { + d3d11CommandBuffer->windowDataCapacity += 1; + d3d11CommandBuffer->windowDatas = SDL_realloc( + d3d11CommandBuffer->windowDatas, + d3d11CommandBuffer->windowDataCapacity * sizeof(D3D11WindowData*) + ); + } + d3d11CommandBuffer->windowDatas[d3d11CommandBuffer->windowDataCount] = windowData; + d3d11CommandBuffer->windowDataCount += 1; + + /* Return the swapchain texture */ + return (Refresh_Texture*) &windowData->textureContainer; +} + +static Refresh_TextureFormat D3D11_GetSwapchainTextureFormat( + Refresh_Renderer *driverData, + SDL_Window *window +) { + D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window); + + if (windowData == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Cannot get swapchain format, window has not been claimed!"); + return 0; + } + + switch (windowData->swapchainFormat) + { + case DXGI_FORMAT_B8G8R8A8_UNORM: + return REFRESH_TEXTUREFORMAT_B8G8R8A8; + + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + return REFRESH_TEXTUREFORMAT_B8G8R8A8_SRGB; + + case DXGI_FORMAT_R16G16B16A16_FLOAT: + return REFRESH_TEXTUREFORMAT_R16G16B16A16_SFLOAT; + + case DXGI_FORMAT_R10G10B10A2_UNORM: + return REFRESH_TEXTUREFORMAT_A2R10G10B10; + + default: + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized swapchain format!"); + return 0; + } +} + +static void D3D11_SetSwapchainParameters( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition, + Refresh_PresentMode presentMode +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11WindowData *windowData = D3D11_INTERNAL_FetchWindowData(window); + + if ( + swapchainComposition != windowData->swapchainComposition || + presentMode != windowData->presentMode + ) { + D3D11_Wait(driverData); + + /* Recreate the swapchain */ + D3D11_INTERNAL_DestroySwapchain( + renderer, + windowData + ); + + D3D11_INTERNAL_CreateSwapchain( + renderer, + windowData, + swapchainComposition, + presentMode + ); + } +} + +/* Submission */ + +static void D3D11_Submit( + Refresh_CommandBuffer *commandBuffer +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Renderer *renderer = (D3D11Renderer*) d3d11CommandBuffer->renderer; + ID3D11CommandList *commandList; + HRESULT res; + + SDL_LockMutex(renderer->contextLock); + + /* Notify the command buffer completion query that we have completed recording */ + ID3D11DeviceContext_End( + renderer->immediateContext, + (ID3D11Asynchronous*) d3d11CommandBuffer->fence->handle + ); + + /* Serialize the commands into the command list */ + res = ID3D11DeviceContext_FinishCommandList( + d3d11CommandBuffer->context, + 0, + &commandList + ); + ERROR_CHECK("Could not finish command list recording!"); + + /* Submit the command list to the immediate context */ + ID3D11DeviceContext_ExecuteCommandList( + renderer->immediateContext, + commandList, + 0 + ); + ID3D11CommandList_Release(commandList); + + /* Mark the command buffer as submitted */ + if (renderer->submittedCommandBufferCount >= renderer->submittedCommandBufferCapacity) + { + renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1; + + renderer->submittedCommandBuffers = SDL_realloc( + renderer->submittedCommandBuffers, + sizeof(D3D11CommandBuffer*) * renderer->submittedCommandBufferCapacity + ); + } + + renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = d3d11CommandBuffer; + renderer->submittedCommandBufferCount += 1; + + /* Present, if applicable */ + for (Uint32 i = 0; i < d3d11CommandBuffer->windowDataCount; i += 1) + { + D3D11WindowData *windowData = d3d11CommandBuffer->windowDatas[i]; + + Uint32 syncInterval = 1; + if (windowData->presentMode == REFRESH_PRESENTMODE_IMMEDIATE || + (renderer->supportsFlipDiscard && windowData->presentMode == REFRESH_PRESENTMODE_MAILBOX) + ) { + syncInterval = 0; + } + + Uint32 presentFlags = 0; + if ( renderer->supportsTearing && + windowData->presentMode == REFRESH_PRESENTMODE_IMMEDIATE ) + { + presentFlags = DXGI_PRESENT_ALLOW_TEARING; + } + + IDXGISwapChain_Present( + windowData->swapchain, + syncInterval, + presentFlags + ); + + ID3D11Texture2D_Release(windowData->texture.handle); + + windowData->inFlightFences[windowData->frameCounter] = d3d11CommandBuffer->fence; + + (void)SDL_AtomicIncRef(&d3d11CommandBuffer->fence->referenceCount); + + windowData->frameCounter = (windowData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT; + } + + /* Check if we can perform any cleanups */ + for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) + { + BOOL queryData; + res = ID3D11DeviceContext_GetData( + renderer->immediateContext, + (ID3D11Asynchronous*) renderer->submittedCommandBuffers[i]->fence->handle, + &queryData, + sizeof(queryData), + 0 + ); + if (res == S_OK) + { + D3D11_INTERNAL_CleanCommandBuffer( + renderer, + renderer->submittedCommandBuffers[i] + ); + } + } + + D3D11_INTERNAL_PerformPendingDestroys(renderer); + + SDL_UnlockMutex(renderer->contextLock); +} + +static Refresh_Fence* D3D11_SubmitAndAcquireFence( + Refresh_CommandBuffer *commandBuffer +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11Fence *fence = d3d11CommandBuffer->fence; + + d3d11CommandBuffer->autoReleaseFence = 0; + D3D11_Submit(commandBuffer); + + return (Refresh_Fence*) fence; +} + +static void D3D11_Wait( + Refresh_Renderer *driverData +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11CommandBuffer *commandBuffer; + + /* + * Wait for all submitted command buffers to complete. + * Sort of equivalent to vkDeviceWaitIdle. + */ + for (Uint32 i = 0; i < renderer->submittedCommandBufferCount; i += 1) + { + D3D11_INTERNAL_WaitForFence( + renderer, + renderer->submittedCommandBuffers[i]->fence + ); + } + + SDL_LockMutex(renderer->contextLock); /* This effectively acts as a lock around submittedCommandBuffers */ + + for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) + { + commandBuffer = renderer->submittedCommandBuffers[i]; + D3D11_INTERNAL_CleanCommandBuffer(renderer, commandBuffer); + } + + D3D11_INTERNAL_PerformPendingDestroys(renderer); + + SDL_UnlockMutex(renderer->contextLock); +} + +/* Queries */ + +static Refresh_OcclusionQuery* D3D11_CreateOcclusionQuery( + Refresh_Renderer *driverData +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11OcclusionQuery *query = (D3D11OcclusionQuery*) SDL_malloc(sizeof(D3D11OcclusionQuery)); + D3D11_QUERY_DESC desc; + HRESULT res; + + desc.Query = D3D11_QUERY_OCCLUSION; + desc.MiscFlags = 0; + + res = ID3D11Device_CreateQuery( + renderer->device, + &desc, + &query->handle + ); + ERROR_CHECK_RETURN("Query creation failed", NULL) + + return (Refresh_OcclusionQuery*) query; +} + +static void D3D11_OcclusionQueryBegin( + Refresh_CommandBuffer *commandBuffer, + Refresh_OcclusionQuery *query +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11OcclusionQuery *d3dQuery = (D3D11OcclusionQuery*) query; + + ID3D11DeviceContext1_Begin( + d3d11CommandBuffer->context, + (ID3D11Asynchronous*) d3dQuery->handle + ); +} + +static void D3D11_OcclusionQueryEnd( + Refresh_CommandBuffer *commandBuffer, + Refresh_OcclusionQuery *query +) { + D3D11CommandBuffer *d3d11CommandBuffer = (D3D11CommandBuffer*) commandBuffer; + D3D11OcclusionQuery *d3dQuery = (D3D11OcclusionQuery*) query; + + ID3D11DeviceContext1_End( + d3d11CommandBuffer->context, + (ID3D11Asynchronous*) d3dQuery->handle + ); +} + +static SDL_bool D3D11_OcclusionQueryPixelCount( + Refresh_Renderer *driverData, + Refresh_OcclusionQuery *query, + Uint32 *pixelCount +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + D3D11OcclusionQuery *d3dQuery = (D3D11OcclusionQuery*) query; + uint64_t result; + HRESULT res; + + SDL_LockMutex(renderer->contextLock); + res = ID3D11DeviceContext_GetData( + renderer->immediateContext, + (ID3D11Asynchronous*) d3dQuery->handle, + &result, + sizeof(result), + 0 + ); + SDL_UnlockMutex(renderer->contextLock); + + *pixelCount = (Uint32) result; + return res == S_OK; +} + +/* Format Info */ + +static SDL_bool D3D11_IsTextureFormatSupported( + Refresh_Renderer *driverData, + Refresh_TextureFormat format, + Refresh_TextureType type, + Refresh_TextureUsageFlags usage +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + DXGI_FORMAT dxgiFormat = RefreshToD3D11_TextureFormat[format]; + DXGI_FORMAT typelessFormat = D3D11_INTERNAL_GetTypelessFormat(dxgiFormat); + UINT formatSupport, sampleableFormatSupport; + HRESULT res; + + res = ID3D11Device_CheckFormatSupport( + renderer->device, + dxgiFormat, + &formatSupport + ); + if (FAILED(res)) + { + /* Format is apparently unknown */ + return SDL_FALSE; + } + + /* Depth textures are stored as typeless textures, but interpreted as color textures for sampling. + * In order to get supported usages for both interpretations, we have to do this. + */ + if (typelessFormat != DXGI_FORMAT_UNKNOWN) + { + res = ID3D11Device_CheckFormatSupport( + renderer->device, + D3D11_INTERNAL_GetSampleableFormat(typelessFormat), + &sampleableFormatSupport + ); + if (SUCCEEDED(res)) + { + formatSupport |= sampleableFormatSupport; + } + } + + /* Is the texture type supported? */ + if (type == REFRESH_TEXTURETYPE_2D && !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D)) + { + return SDL_FALSE; + } + if (type == REFRESH_TEXTURETYPE_3D && !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE3D)) + { + return SDL_FALSE; + } + if (type == REFRESH_TEXTURETYPE_CUBE && !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURECUBE)) + { + return SDL_FALSE; + } + + /* Are the usage flags supported? */ + if ((usage & REFRESH_TEXTUREUSAGE_SAMPLER_BIT) && !(formatSupport & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE)) + { + return SDL_FALSE; + } + if ((usage & (REFRESH_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT | REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT | REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT)) && !(formatSupport & D3D11_FORMAT_SUPPORT_SHADER_LOAD)) + { + return SDL_FALSE; + } + if ((usage & REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT) && !(formatSupport & D3D11_FORMAT_SUPPORT_RENDER_TARGET)) + { + return SDL_FALSE; + } + if ((usage & REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) && !(formatSupport & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL)) + { + return SDL_FALSE; + } + + return SDL_TRUE; +} + +static Refresh_SampleCount D3D11_GetBestSampleCount( + Refresh_Renderer *driverData, + Refresh_TextureFormat format, + Refresh_SampleCount desiredSampleCount +) { + D3D11Renderer *renderer = (D3D11Renderer*) driverData; + Refresh_SampleCount maxSupported = REFRESH_SAMPLECOUNT_8; + Uint32 levels; + HRESULT res; + + while (maxSupported > REFRESH_SAMPLECOUNT_1) + { + res = ID3D11Device_CheckMultisampleQualityLevels( + renderer->device, + RefreshToD3D11_TextureFormat[format], + RefreshToD3D11_SampleCount[desiredSampleCount], + &levels + ); + if (SUCCEEDED(res) && levels > 0) + { + break; + } + maxSupported -= 1; + } + + return (Refresh_SampleCount) SDL_min(maxSupported, desiredSampleCount); +} + +/* SPIR-V Cross Interop */ + +extern Refresh_Shader* D3D11_CompileFromSPIRVCross( + Refresh_Renderer *driverData, + Refresh_ShaderStage shader_stage, + const char *entryPointName, + const char *source +); + +/* Device Creation */ + +static SDL_bool D3D11_PrepareDriver() +{ + void *d3d11_dll, *dxgi_dll; + PFN_D3D11_CREATE_DEVICE D3D11CreateDeviceFunc; + D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_1 }; + PFN_CREATE_DXGI_FACTORY1 CreateDXGIFactoryFunc; + HRESULT res; + + /* Can we load D3D11? */ + + d3d11_dll = SDL_LoadObject(D3D11_DLL); + if (d3d11_dll == NULL) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11: Could not find " D3D11_DLL); + return 0; + } + + D3D11CreateDeviceFunc = (PFN_D3D11_CREATE_DEVICE) SDL_LoadFunction( + d3d11_dll, + D3D11_CREATE_DEVICE_FUNC + ); + if (D3D11CreateDeviceFunc == NULL) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11: Could not find function " D3D11_CREATE_DEVICE_FUNC " in " D3D11_DLL); + SDL_UnloadObject(d3d11_dll); + return 0; + } + + /* Can we create a device? */ + + res = D3D11CreateDeviceFunc( + NULL, + D3D_DRIVER_TYPE_HARDWARE, + NULL, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + levels, + SDL_arraysize(levels), + D3D11_SDK_VERSION, + NULL, + NULL, + NULL + ); + + SDL_UnloadObject(d3d11_dll); + + if (FAILED(res)) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11: Could not create D3D11Device with feature level 11_0"); + return 0; + } + + /* Can we load DXGI? */ + + dxgi_dll = SDL_LoadObject(DXGI_DLL); + if (dxgi_dll == NULL) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11: Could not find " DXGI_DLL); + return 0; + } + + CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY1) SDL_LoadFunction( + dxgi_dll, + CREATE_DXGI_FACTORY1_FUNC + ); + SDL_UnloadObject(dxgi_dll); /* We're not going to call this function, so we can just unload now. */ + if (CreateDXGIFactoryFunc == NULL) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "D3D11: Could not find function " CREATE_DXGI_FACTORY1_FUNC " in " DXGI_DLL); + return 0; + } + + return 1; +} + +static void D3D11_INTERNAL_TryInitializeDXGIDebug(D3D11Renderer *renderer) +{ + PFN_DXGI_GET_DEBUG_INTERFACE DXGIGetDebugInterfaceFunc; + HRESULT res; + + renderer->dxgidebug_dll = SDL_LoadObject(DXGIDEBUG_DLL); + if (renderer->dxgidebug_dll == NULL) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not find " DXGIDEBUG_DLL); + return; + } + + DXGIGetDebugInterfaceFunc = (PFN_DXGI_GET_DEBUG_INTERFACE) SDL_LoadFunction( + renderer->dxgidebug_dll, + DXGI_GET_DEBUG_INTERFACE_FUNC + ); + if (DXGIGetDebugInterfaceFunc == NULL) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " DXGI_GET_DEBUG_INTERFACE_FUNC); + return; + } + + res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIDebug, (void**) &renderer->dxgiDebug); + if (FAILED(res)) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get IDXGIDebug interface"); + } + +#ifdef HAVE_IDXGIINFOQUEUE + res = DXGIGetDebugInterfaceFunc(&D3D_IID_IDXGIInfoQueue, (void**) &renderer->dxgiInfoQueue); + if (FAILED(res)) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not get IDXGIInfoQueue interface"); + } +#endif +} + +static void D3D11_INTERNAL_InitBlitPipelines( + D3D11Renderer *renderer +) { + Refresh_ShaderCreateInfo shaderModuleCreateInfo; + Refresh_GraphicsPipelineCreateInfo blitPipelineCreateInfo; + Refresh_SamplerCreateInfo samplerCreateInfo; + Refresh_VertexBinding binding; + Refresh_VertexAttribute attribute; + Refresh_ColorAttachmentDescription colorAttachmentDesc; + + /* Fullscreen vertex shader */ + shaderModuleCreateInfo.code = (Uint8*) D3D11_FullscreenVert; + shaderModuleCreateInfo.codeSize = sizeof(D3D11_FullscreenVert); + shaderModuleCreateInfo.stage = REFRESH_SHADERSTAGE_VERTEX; + shaderModuleCreateInfo.format = REFRESH_SHADERFORMAT_DXBC; + shaderModuleCreateInfo.entryPointName = "main"; + + renderer->fullscreenVertexShader = D3D11_CreateShader( + (Refresh_Renderer*) renderer, + &shaderModuleCreateInfo + ); + + if (renderer->fullscreenVertexShader == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile fullscreen vertex shader!"); + } + + /* Blit from 2D pixel shader */ + shaderModuleCreateInfo.code = (Uint8*) D3D11_BlitFrom2D; + shaderModuleCreateInfo.codeSize = sizeof(D3D11_BlitFrom2D); + shaderModuleCreateInfo.stage = REFRESH_SHADERSTAGE_FRAGMENT; + + renderer->blitFrom2DPixelShader = D3D11_CreateShader( + (Refresh_Renderer*) renderer, + &shaderModuleCreateInfo + ); + + if (renderer->blitFrom2DPixelShader == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile blit from 2D pixel shader!"); + } + + /* Blit from 2D array pixel shader */ + shaderModuleCreateInfo.code = (Uint8 *) D3D11_BlitFrom2DArray; + shaderModuleCreateInfo.codeSize = sizeof(D3D11_BlitFrom2DArray); + shaderModuleCreateInfo.stage = REFRESH_SHADERSTAGE_FRAGMENT; + + renderer->blitFrom2DArrayPixelShader = D3D11_CreateShader( + (Refresh_Renderer*) renderer, + &shaderModuleCreateInfo + ); + + if (renderer->blitFrom2DArrayPixelShader == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile blit from 2D array pixel shader!"); + } + + /* Blit from 2D pipeline */ + SDL_memset(&colorAttachmentDesc, '\0', sizeof(Refresh_ColorAttachmentDescription)); + colorAttachmentDesc.blendState.blendEnable = 0; + colorAttachmentDesc.blendState.colorWriteMask = + REFRESH_COLORCOMPONENT_R_BIT | + REFRESH_COLORCOMPONENT_G_BIT | + REFRESH_COLORCOMPONENT_B_BIT | + REFRESH_COLORCOMPONENT_A_BIT; + + colorAttachmentDesc.format = REFRESH_TEXTUREFORMAT_R8G8B8A8; /* format doesn't matter in d3d11 */ + + blitPipelineCreateInfo.attachmentInfo.colorAttachmentDescriptions = &colorAttachmentDesc; + blitPipelineCreateInfo.attachmentInfo.colorAttachmentCount = 1; + blitPipelineCreateInfo.attachmentInfo.depthStencilFormat = REFRESH_TEXTUREFORMAT_D16_UNORM; /* arbitrary */ + blitPipelineCreateInfo.attachmentInfo.hasDepthStencilAttachment = 0; + + SDL_memset(&blitPipelineCreateInfo.depthStencilState, '\0', sizeof(Refresh_DepthStencilState)); + blitPipelineCreateInfo.depthStencilState.depthTestEnable = 0; + blitPipelineCreateInfo.depthStencilState.depthWriteEnable = 0; + blitPipelineCreateInfo.depthStencilState.stencilTestEnable = 0; + + binding.binding = 0; + binding.inputRate = REFRESH_VERTEXINPUTRATE_VERTEX; + binding.stepRate = 0; + binding.stride = 64; + + attribute.binding = 0; + attribute.format = REFRESH_VERTEXELEMENTFORMAT_VECTOR2; + attribute.location = 0; + attribute.offset = 0; + + blitPipelineCreateInfo.vertexInputState.vertexAttributeCount = 1; + blitPipelineCreateInfo.vertexInputState.vertexAttributes = &attribute; + blitPipelineCreateInfo.vertexInputState.vertexBindingCount = 1; + blitPipelineCreateInfo.vertexInputState.vertexBindings = &binding; + + blitPipelineCreateInfo.vertexShader = renderer->fullscreenVertexShader; + blitPipelineCreateInfo.fragmentShader = renderer->blitFrom2DPixelShader; + + blitPipelineCreateInfo.multisampleState.multisampleCount = REFRESH_SAMPLECOUNT_1; + blitPipelineCreateInfo.multisampleState.sampleMask = 0xFFFFFFFF; + + blitPipelineCreateInfo.rasterizerState.cullMode = REFRESH_CULLMODE_NONE; + blitPipelineCreateInfo.rasterizerState.fillMode = REFRESH_FILLMODE_FILL; + blitPipelineCreateInfo.rasterizerState.frontFace = REFRESH_FRONTFACE_CLOCKWISE; + blitPipelineCreateInfo.rasterizerState.depthBiasEnable = 0; + blitPipelineCreateInfo.rasterizerState.depthBiasClamp = 0.0f; + blitPipelineCreateInfo.rasterizerState.depthBiasConstantFactor = 0.0f; + blitPipelineCreateInfo.rasterizerState.depthBiasSlopeFactor = 0.0f; + + blitPipelineCreateInfo.primitiveType = REFRESH_PRIMITIVETYPE_TRIANGLELIST; + + blitPipelineCreateInfo.blendConstants[0] = 1.0f; + blitPipelineCreateInfo.blendConstants[1] = 1.0f; + blitPipelineCreateInfo.blendConstants[2] = 1.0f; + blitPipelineCreateInfo.blendConstants[3] = 1.0f; + + blitPipelineCreateInfo.vertexResourceInfo.samplerCount = 0; + blitPipelineCreateInfo.vertexResourceInfo.storageTextureCount = 0; + blitPipelineCreateInfo.vertexResourceInfo.storageBufferCount = 0; + blitPipelineCreateInfo.vertexResourceInfo.uniformBufferCount = 0; + + blitPipelineCreateInfo.fragmentResourceInfo.samplerCount = 1; + blitPipelineCreateInfo.fragmentResourceInfo.storageTextureCount = 0; + blitPipelineCreateInfo.fragmentResourceInfo.storageBufferCount = 0; + blitPipelineCreateInfo.fragmentResourceInfo.uniformBufferCount = 0; + + renderer->blitFrom2DPipeline = D3D11_CreateGraphicsPipeline( + (Refresh_Renderer*) renderer, + &blitPipelineCreateInfo + ); + + if (renderer->blitFrom2DPipeline == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create blit pipeline!"); + } + + /* Blit from 2D array pipeline */ + blitPipelineCreateInfo.fragmentShader = renderer->blitFrom2DArrayPixelShader; + + blitPipelineCreateInfo.fragmentResourceInfo.uniformBufferCount = 1; + + renderer->blitFrom2DArrayPipeline = D3D11_CreateGraphicsPipeline( + (Refresh_Renderer*) renderer, + &blitPipelineCreateInfo + ); + + /* Create samplers */ + samplerCreateInfo.addressModeU = REFRESH_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeV = REFRESH_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeW = REFRESH_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; + samplerCreateInfo.anisotropyEnable = 0; + samplerCreateInfo.compareEnable = 0; + samplerCreateInfo.magFilter = REFRESH_FILTER_NEAREST; + samplerCreateInfo.minFilter = REFRESH_FILTER_NEAREST; + samplerCreateInfo.mipmapMode = REFRESH_SAMPLERMIPMAPMODE_NEAREST; + samplerCreateInfo.mipLodBias = 0.0f; + samplerCreateInfo.minLod = 0; + samplerCreateInfo.maxLod = 1000; + samplerCreateInfo.borderColor = REFRESH_BORDERCOLOR_FLOAT_TRANSPARENT_BLACK; + + renderer->blitNearestSampler = D3D11_CreateSampler( + (Refresh_Renderer*) renderer, + &samplerCreateInfo + ); + + if (renderer->blitNearestSampler == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create blit nearest sampler!"); + } + + samplerCreateInfo.magFilter = REFRESH_FILTER_LINEAR; + samplerCreateInfo.minFilter = REFRESH_FILTER_LINEAR; + samplerCreateInfo.mipmapMode = REFRESH_SAMPLERMIPMAPMODE_LINEAR; + + renderer->blitLinearSampler = D3D11_CreateSampler( + (Refresh_Renderer*) renderer, + &samplerCreateInfo + ); + + if (renderer->blitLinearSampler == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create blit linear sampler!"); + } +} + +static void D3D11_INTERNAL_DestroyBlitPipelines( + Refresh_Renderer *driverData +) { + D3D11Renderer *renderer = (D3D11Renderer *)driverData; + + D3D11_ReleaseSampler(driverData, renderer->blitLinearSampler); + D3D11_ReleaseSampler(driverData, renderer->blitNearestSampler); + + D3D11_ReleaseGraphicsPipeline(driverData, renderer->blitFrom2DPipeline); + D3D11_ReleaseGraphicsPipeline(driverData, renderer->blitFrom2DArrayPipeline); + + D3D11_ReleaseShader(driverData, renderer->fullscreenVertexShader); + D3D11_ReleaseShader(driverData, renderer->blitFrom2DPixelShader); + D3D11_ReleaseShader(driverData, renderer->blitFrom2DArrayPixelShader); +} + +static Refresh_Device* D3D11_CreateDevice(SDL_bool debugMode) +{ + D3D11Renderer *renderer; + PFN_CREATE_DXGI_FACTORY1 CreateDXGIFactoryFunc; + PFN_D3D11_CREATE_DEVICE D3D11CreateDeviceFunc; + D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_1 }; + IDXGIFactory4 *factory4; + IDXGIFactory5 *factory5; + IDXGIFactory6 *factory6; + Uint32 flags; + DXGI_ADAPTER_DESC1 adapterDesc; + HRESULT res; + Refresh_Device* result; + + /* Allocate and zero out the renderer */ + renderer = (D3D11Renderer*) SDL_calloc(1, sizeof(D3D11Renderer)); + + /* Load the DXGI library */ + renderer->dxgi_dll = SDL_LoadObject(DXGI_DLL); + if (renderer->dxgi_dll == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not find " DXGI_DLL); + return NULL; + } + + /* Load the CreateDXGIFactory1 function */ + CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY1) SDL_LoadFunction( + renderer->dxgi_dll, + CREATE_DXGI_FACTORY1_FUNC + ); + if (CreateDXGIFactoryFunc == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " CREATE_DXGI_FACTORY1_FUNC); + return NULL; + } + + /* Create the DXGI factory */ + res = CreateDXGIFactoryFunc( + &D3D_IID_IDXGIFactory1, + (void**) &renderer->factory + ); + ERROR_CHECK_RETURN("Could not create DXGIFactory", NULL); + + /* Check for flip-model discard support (supported on Windows 10+) */ + res = IDXGIFactory1_QueryInterface( + renderer->factory, + &D3D_IID_IDXGIFactory4, + (void**) &factory4 + ); + if (SUCCEEDED(res)) + { + renderer->supportsFlipDiscard = 1; + IDXGIFactory4_Release(factory4); + } + + /* Check for explicit tearing support */ + res = IDXGIFactory1_QueryInterface( + renderer->factory, + &D3D_IID_IDXGIFactory5, + (void**) &factory5 + ); + if (SUCCEEDED(res)) + { + res = IDXGIFactory5_CheckFeatureSupport( + factory5, + DXGI_FEATURE_PRESENT_ALLOW_TEARING, + &renderer->supportsTearing, + sizeof(renderer->supportsTearing) + ); + if (FAILED(res)) + { + renderer->supportsTearing = FALSE; + } + IDXGIFactory5_Release(factory5); + } + + /* Select the appropriate device for rendering */ + res = IDXGIAdapter1_QueryInterface( + renderer->factory, + &D3D_IID_IDXGIFactory6, + (void**) &factory6 + ); + if (SUCCEEDED(res)) + { + IDXGIFactory6_EnumAdapterByGpuPreference( + factory6, + 0, + DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, + &D3D_IID_IDXGIAdapter1, + (void**) &renderer->adapter + ); + IDXGIFactory6_Release(factory6); + } + else + { + IDXGIFactory1_EnumAdapters1( + renderer->factory, + 0, + &renderer->adapter + ); + } + + /* Get information about the selected adapter. Used for logging info. */ + IDXGIAdapter1_GetDesc1(renderer->adapter, &adapterDesc); + + /* Initialize the DXGI debug layer, if applicable */ + if (debugMode) + { + D3D11_INTERNAL_TryInitializeDXGIDebug(renderer); + } + + /* Load the D3D library */ + renderer->d3d11_dll = SDL_LoadObject(D3D11_DLL); + if (renderer->d3d11_dll == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not find " D3D11_DLL); + return NULL; + } + + /* Load the CreateDevice function */ + D3D11CreateDeviceFunc = (PFN_D3D11_CREATE_DEVICE) SDL_LoadFunction( + renderer->d3d11_dll, + D3D11_CREATE_DEVICE_FUNC + ); + if (D3D11CreateDeviceFunc == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not load function: " D3D11_CREATE_DEVICE_FUNC); + return NULL; + } + + /* Set up device flags */ + flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + if (debugMode) + { + flags |= D3D11_CREATE_DEVICE_DEBUG; + } + + /* Create the device */ + ID3D11Device *d3d11Device; +tryCreateDevice: + res = D3D11CreateDeviceFunc( + (IDXGIAdapter*) renderer->adapter, + D3D_DRIVER_TYPE_UNKNOWN, /* Must be UNKNOWN if adapter is non-null according to spec */ + NULL, + flags, + levels, + SDL_arraysize(levels), + D3D11_SDK_VERSION, + &d3d11Device, + NULL, + &renderer->immediateContext + ); + if (FAILED(res) && debugMode) + { + /* If device creation failed, and we're in debug mode, remove the debug flag and try again. */ + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Creating device in debug mode failed with error " HRESULT_FMT ". Trying non-debug.", res); + flags &= ~D3D11_CREATE_DEVICE_DEBUG; + debugMode = 0; + goto tryCreateDevice; + } + + ERROR_CHECK_RETURN("Could not create D3D11 device", NULL); + + /* The actual device we want is the ID3D11Device1 interface... */ + res = ID3D11Device_QueryInterface( + d3d11Device, + &D3D_IID_ID3D11Device1, + (void**) &renderer->device + ); + ERROR_CHECK_RETURN("Could not get ID3D11Device1 interface", NULL); + + /* Release the old device interface, we don't need it anymore */ + ID3D11Device_Release(d3d11Device); + +#ifdef HAVE_IDXGIINFOQUEUE + /* Set up the info queue */ + if (renderer->dxgiInfoQueue) + { + DXGI_INFO_QUEUE_MESSAGE_SEVERITY sevList[] = + { + DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, + DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, + DXGI_INFO_QUEUE_MESSAGE_SEVERITY_WARNING, + // DXGI_INFO_QUEUE_MESSAGE_SEVERITY_INFO, /* This can be a bit much, so toggle as needed for debugging. */ + DXGI_INFO_QUEUE_MESSAGE_SEVERITY_MESSAGE + }; + DXGI_INFO_QUEUE_FILTER filter = { 0 }; + filter.AllowList.NumSeverities = SDL_arraysize(sevList); + filter.AllowList.pSeverityList = sevList; + + IDXGIInfoQueue_PushStorageFilter( + renderer->dxgiInfoQueue, + D3D_IID_DXGI_DEBUG_ALL, + &filter + ); + } +#endif + + /* Print driver info */ + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "SDL GPU Driver: D3D11"); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "D3D11 Adapter: %S", adapterDesc.Description); + + /* Create mutexes */ + renderer->contextLock = SDL_CreateMutex(); + renderer->acquireCommandBufferLock = SDL_CreateMutex(); + renderer->fenceLock = SDL_CreateMutex(); + renderer->windowLock = SDL_CreateMutex(); + + /* Initialize miscellaneous renderer members */ + renderer->debugMode = (flags & D3D11_CREATE_DEVICE_DEBUG); + + /* Initialize SetStringMarker support, if available */ + res = ID3D11DeviceContext_QueryInterface( + renderer->immediateContext, + &D3D_IID_ID3DUserDefinedAnnotation, + (void**) &renderer->annotation + ); + + if (res < 0) + { + D3D11_INTERNAL_LogError(renderer->device, "Could not get UserDefinedAnnotation", res); + renderer->annotation = NULL; + } + + /* Create command buffer pool */ + D3D11_INTERNAL_AllocateCommandBuffers(renderer, 2); + + /* Create fence pool */ + renderer->availableFenceCapacity = 2; + renderer->availableFences = SDL_malloc( + sizeof(D3D11Fence*) * renderer->availableFenceCapacity + ); + + /* Create deferred destroy arrays */ + renderer->transferBufferContainersToDestroyCapacity = 2; + renderer->transferBufferContainersToDestroyCount = 0; + renderer->transferBufferContainersToDestroy = SDL_malloc( + renderer->transferBufferContainersToDestroyCapacity * sizeof(D3D11TransferBufferContainer*) + ); + + renderer->bufferContainersToDestroyCapacity = 2; + renderer->bufferContainersToDestroyCount = 0; + renderer->bufferContainersToDestroy = SDL_malloc( + renderer->bufferContainersToDestroyCapacity * sizeof(D3D11BufferContainer*) + ); + + renderer->textureContainersToDestroyCapacity = 2; + renderer->textureContainersToDestroyCount = 0; + renderer->textureContainersToDestroy = SDL_malloc( + renderer->textureContainersToDestroyCapacity * sizeof(D3D11TextureContainer*) + ); + + /* Create claimed window list */ + renderer->claimedWindowCapacity = 1; + renderer->claimedWindows = SDL_malloc( + sizeof(D3D11WindowData*) * renderer->claimedWindowCapacity + ); + + /* Initialize null states */ + + SDL_zeroa(nullRTVs); + SDL_zeroa(nullSRVs); + SDL_zeroa(nullSamplers); + SDL_zeroa(nullUAVs); + + /* Initialize built-in pipelines */ + D3D11_INTERNAL_InitBlitPipelines(renderer); + + /* Create the Refresh_ Device */ + result = (Refresh_Device*) SDL_malloc(sizeof(Refresh_Device)); + ASSIGN_DRIVER(D3D11) + result->driverData = (Refresh_Renderer*) renderer; + + return result; +} + +Refresh_Driver D3D11Driver = { + "D3D11", + REFRESH_BACKEND_D3D11, + D3D11_PrepareDriver, + D3D11_CreateDevice +}; + +#endif /* REFRESH_D3D11 */ diff --git a/src/d3d11/Refresh_d3d11_d3dcompiler.c b/src/d3d11/Refresh_d3d11_d3dcompiler.c new file mode 100644 index 0000000..6401717 --- /dev/null +++ b/src/d3d11/Refresh_d3d11_d3dcompiler.c @@ -0,0 +1,207 @@ +/* Refresh - a cross-platform hardware-accelerated graphics library with modern capabilities + * + * Copyright (c) 2020-2024 Evan Hemsley + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Evan "cosmonaut" Hemsley + * + */ + +#if REFRESH_D3D11 + +#include "Refresh_driver.h" + +#define CINTERFACE +#define COBJMACROS +#include + +/* __stdcall declaration, largely taken from vkd3d_windows.h */ +#ifdef _WIN32 +#define D3DCOMPILER_API __stdcall +#else +# ifdef __stdcall +# undef __stdcall +# endif +# ifdef __x86_64__ +# define __stdcall __attribute__((ms_abi)) +# else +# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || defined(__APPLE__) +# define __stdcall __attribute__((__stdcall__)) __attribute__((__force_align_arg_pointer__)) +# else +# define __stdcall __attribute__((__stdcall__)) +# endif +# endif +# define D3DCOMPILER_API __stdcall +#endif + +/* vkd3d uses stdcall for its ID3D10Blob implementation */ +#ifndef _WIN32 +typedef struct VKD3DBlob VKD3DBlob; +typedef struct VKD3DBlobVtbl +{ + HRESULT(__stdcall* QueryInterface)( + VKD3DBlob* This, + REFIID riid, + void** ppvObject); + ULONG(__stdcall* AddRef)(VKD3DBlob* This); + ULONG(__stdcall* Release)(VKD3DBlob* This); + LPVOID(__stdcall* GetBufferPointer)(VKD3DBlob* This); + SIZE_T(__stdcall* GetBufferSize)(VKD3DBlob* This); +} VKD3DBlobVtbl; +struct VKD3DBlob +{ + const VKD3DBlobVtbl* lpVtbl; +}; +#define ID3D10Blob VKD3DBlob +#define ID3DBlob VKD3DBlob +#endif + +/* rename the DLL for different platforms */ +#if defined(WIN32) +#undef D3DCOMPILER_DLL +#define D3DCOMPILER_DLL D3DCOMPILER_DLL_A +#elif defined(__APPLE__) +#undef D3DCOMPILER_DLL +#define D3DCOMPILER_DLL "libvkd3d-utils.1.dylib" +#else +#undef D3DCOMPILER_DLL +#define D3DCOMPILER_DLL "libvkd3d-utils.so.1" +#endif + +/* D3DCompile signature */ +typedef HRESULT(D3DCOMPILER_API* PFN_D3DCOMPILE)( + LPCVOID pSrcData, + SIZE_T SrcDataSize, + LPCSTR pSourceName, + const D3D_SHADER_MACRO* pDefines, + ID3DInclude* pInclude, + LPCSTR pEntrypoint, + LPCSTR pTarget, + UINT Flags1, + UINT Flags2, + ID3DBlob** ppCode, + ID3DBlob** ppErrorMsgs +); + +static void* d3dcompiler_dll; +static PFN_D3DCOMPILE D3DCompile_func; + +#if 0 /* TODO */ +static void D3D11_Quit(void) +{ + if (d3dcompiler_dll) { + SDL_UnloadObject(d3dcompiler_dll); + } + d3dcompiler_dll = NULL; + D3DCompile_func = NULL; +} +#endif + +extern Refresh_Shader* D3D11_CreateShader( + Refresh_Renderer *driverData, + Refresh_ShaderCreateInfo *shaderCreateInfo +); + +Refresh_Shader* D3D11_CompileFromSPIRVCross( + Refresh_Renderer *driverData, + Refresh_ShaderStage shader_stage, + const char *entryPointName, + const char *source +) { + HRESULT result; + ID3DBlob *blob; + ID3DBlob *error_blob; + Refresh_ShaderCreateInfo createInfo; + Refresh_Shader *shader; + const char *profile; + + /* FIXME: d3dcompiler could probably be loaded in a better spot */ + + /* Load the DLL if we haven't already */ + if (!d3dcompiler_dll) { + d3dcompiler_dll = SDL_LoadObject(D3DCOMPILER_DLL); + if (!d3dcompiler_dll) { + SDL_SetError("Failed to load " D3DCOMPILER_DLL); + return NULL; + } + } + + /* Load the D3DCompile function if we haven't already */ + if (!D3DCompile_func) { + D3DCompile_func = (PFN_D3DCOMPILE) SDL_LoadFunction(d3dcompiler_dll, "D3DCompile"); + if (!D3DCompile_func) { + SDL_SetError("Failed to load D3DCompile function"); + return NULL; + } + } + + if (shader_stage == REFRESH_SHADERSTAGE_VERTEX) + { + profile = "vs_5_0"; + } + else if (shader_stage == REFRESH_SHADERSTAGE_FRAGMENT) + { + profile = "ps_5_0"; + } + else if (shader_stage == REFRESH_SHADERSTAGE_COMPUTE) + { + profile = "cs_5_0"; + } + else + { + SDL_SetError("%s", "Unrecognized shader stage!"); + return NULL; + } + + /* Compile! */ + result = D3DCompile_func( + source, + SDL_strlen(source), + NULL, + NULL, + NULL, + "main", /* entry point name ignored */ + profile, + 0, + 0, + &blob, + &error_blob + ); + if (result < 0) { + SDL_SetError("%s", (const char*) ID3D10Blob_GetBufferPointer(error_blob)); + ID3D10Blob_Release(error_blob); + return NULL; + } + + /* Create the shader */ + createInfo.code = ID3D10Blob_GetBufferPointer(blob); + createInfo.codeSize = ID3D10Blob_GetBufferSize(blob); + createInfo.format = REFRESH_SHADERFORMAT_DXBC; + createInfo.stage = shader_stage; + createInfo.entryPointName = entryPointName; + shader = D3D11_CreateShader(driverData, &createInfo); + + /* Clean up */ + ID3D10Blob_Release(blob); + + return shader; +} + +#endif /* REFRESH_D3D11 */ diff --git a/src/spirv.h b/src/spirv.h new file mode 100644 index 0000000..9ab3e5d --- /dev/null +++ b/src/spirv.h @@ -0,0 +1,2893 @@ +/* +** Copyright (c) 2014-2024 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python, C#, D, Beef +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** - C# will use enum classes in the Specification class located in the "Spv" namespace, +** e.g.: Spv.Specification.SourceLanguage.GLSL +** - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +** - Beef will use enum classes in the Specification class located in the "Spv" namespace, +** e.g.: Spv.Specification.SourceLanguage.GLSL +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10600 +#define SPV_REVISION 1 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010600; +static const unsigned int SpvRevision = 1; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageCPP_for_OpenCL = 6, + SpvSourceLanguageSYCL = 7, + SpvSourceLanguageHERO_C = 8, + SpvSourceLanguageNZSL = 9, + SpvSourceLanguageWGSL = 10, + SpvSourceLanguageSlang = 11, + SpvSourceLanguageZig = 12, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelTaskNV = 5267, + SpvExecutionModelMeshNV = 5268, + SpvExecutionModelRayGenerationKHR = 5313, + SpvExecutionModelRayGenerationNV = 5313, + SpvExecutionModelIntersectionKHR = 5314, + SpvExecutionModelIntersectionNV = 5314, + SpvExecutionModelAnyHitKHR = 5315, + SpvExecutionModelAnyHitNV = 5315, + SpvExecutionModelClosestHitKHR = 5316, + SpvExecutionModelClosestHitNV = 5316, + SpvExecutionModelMissKHR = 5317, + SpvExecutionModelMissNV = 5317, + SpvExecutionModelCallableKHR = 5318, + SpvExecutionModelCallableNV = 5318, + SpvExecutionModelTaskEXT = 5364, + SpvExecutionModelMeshEXT = 5365, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelPhysicalStorageBuffer64 = 5348, + SpvAddressingModelPhysicalStorageBuffer64EXT = 5348, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelVulkan = 3, + SpvMemoryModelVulkanKHR = 3, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModeInitializer = 33, + SpvExecutionModeFinalizer = 34, + SpvExecutionModeSubgroupSize = 35, + SpvExecutionModeSubgroupsPerWorkgroup = 36, + SpvExecutionModeSubgroupsPerWorkgroupId = 37, + SpvExecutionModeLocalSizeId = 38, + SpvExecutionModeLocalSizeHintId = 39, + SpvExecutionModeNonCoherentColorAttachmentReadEXT = 4169, + SpvExecutionModeNonCoherentDepthAttachmentReadEXT = 4170, + SpvExecutionModeNonCoherentStencilAttachmentReadEXT = 4171, + SpvExecutionModeSubgroupUniformControlFlowKHR = 4421, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeDenormPreserve = 4459, + SpvExecutionModeDenormFlushToZero = 4460, + SpvExecutionModeSignedZeroInfNanPreserve = 4461, + SpvExecutionModeRoundingModeRTE = 4462, + SpvExecutionModeRoundingModeRTZ = 4463, + SpvExecutionModeEarlyAndLateFragmentTestsAMD = 5017, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeCoalescingAMDX = 5069, + SpvExecutionModeMaxNodeRecursionAMDX = 5071, + SpvExecutionModeStaticNumWorkgroupsAMDX = 5072, + SpvExecutionModeShaderIndexAMDX = 5073, + SpvExecutionModeMaxNumWorkgroupsAMDX = 5077, + SpvExecutionModeStencilRefUnchangedFrontAMD = 5079, + SpvExecutionModeStencilRefGreaterFrontAMD = 5080, + SpvExecutionModeStencilRefLessFrontAMD = 5081, + SpvExecutionModeStencilRefUnchangedBackAMD = 5082, + SpvExecutionModeStencilRefGreaterBackAMD = 5083, + SpvExecutionModeStencilRefLessBackAMD = 5084, + SpvExecutionModeQuadDerivativesKHR = 5088, + SpvExecutionModeRequireFullQuadsKHR = 5089, + SpvExecutionModeOutputLinesEXT = 5269, + SpvExecutionModeOutputLinesNV = 5269, + SpvExecutionModeOutputPrimitivesEXT = 5270, + SpvExecutionModeOutputPrimitivesNV = 5270, + SpvExecutionModeDerivativeGroupQuadsNV = 5289, + SpvExecutionModeDerivativeGroupLinearNV = 5290, + SpvExecutionModeOutputTrianglesEXT = 5298, + SpvExecutionModeOutputTrianglesNV = 5298, + SpvExecutionModePixelInterlockOrderedEXT = 5366, + SpvExecutionModePixelInterlockUnorderedEXT = 5367, + SpvExecutionModeSampleInterlockOrderedEXT = 5368, + SpvExecutionModeSampleInterlockUnorderedEXT = 5369, + SpvExecutionModeShadingRateInterlockOrderedEXT = 5370, + SpvExecutionModeShadingRateInterlockUnorderedEXT = 5371, + SpvExecutionModeSharedLocalMemorySizeINTEL = 5618, + SpvExecutionModeRoundingModeRTPINTEL = 5620, + SpvExecutionModeRoundingModeRTNINTEL = 5621, + SpvExecutionModeFloatingPointModeALTINTEL = 5622, + SpvExecutionModeFloatingPointModeIEEEINTEL = 5623, + SpvExecutionModeMaxWorkgroupSizeINTEL = 5893, + SpvExecutionModeMaxWorkDimINTEL = 5894, + SpvExecutionModeNoGlobalOffsetINTEL = 5895, + SpvExecutionModeNumSIMDWorkitemsINTEL = 5896, + SpvExecutionModeSchedulerTargetFmaxMhzINTEL = 5903, + SpvExecutionModeMaximallyReconvergesKHR = 6023, + SpvExecutionModeFPFastMathDefault = 6028, + SpvExecutionModeStreamingInterfaceINTEL = 6154, + SpvExecutionModeRegisterMapInterfaceINTEL = 6160, + SpvExecutionModeNamedBarrierCountINTEL = 6417, + SpvExecutionModeMaximumRegistersINTEL = 6461, + SpvExecutionModeMaximumRegistersIdINTEL = 6462, + SpvExecutionModeNamedMaximumRegistersINTEL = 6463, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassTileImageEXT = 4172, + SpvStorageClassNodePayloadAMDX = 5068, + SpvStorageClassNodeOutputPayloadAMDX = 5076, + SpvStorageClassCallableDataKHR = 5328, + SpvStorageClassCallableDataNV = 5328, + SpvStorageClassIncomingCallableDataKHR = 5329, + SpvStorageClassIncomingCallableDataNV = 5329, + SpvStorageClassRayPayloadKHR = 5338, + SpvStorageClassRayPayloadNV = 5338, + SpvStorageClassHitAttributeKHR = 5339, + SpvStorageClassHitAttributeNV = 5339, + SpvStorageClassIncomingRayPayloadKHR = 5342, + SpvStorageClassIncomingRayPayloadNV = 5342, + SpvStorageClassShaderRecordBufferKHR = 5343, + SpvStorageClassShaderRecordBufferNV = 5343, + SpvStorageClassPhysicalStorageBuffer = 5349, + SpvStorageClassPhysicalStorageBufferEXT = 5349, + SpvStorageClassHitObjectAttributeNV = 5385, + SpvStorageClassTaskPayloadWorkgroupEXT = 5402, + SpvStorageClassCodeSectionINTEL = 5605, + SpvStorageClassDeviceOnlyINTEL = 5936, + SpvStorageClassHostOnlyINTEL = 5937, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimTileImageDataEXT = 4173, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatR64ui = 40, + SpvImageFormatR64i = 41, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeUnsignedIntRaw10EXT = 19, + SpvImageChannelDataTypeUnsignedIntRaw12EXT = 20, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMakeTexelAvailableShift = 8, + SpvImageOperandsMakeTexelAvailableKHRShift = 8, + SpvImageOperandsMakeTexelVisibleShift = 9, + SpvImageOperandsMakeTexelVisibleKHRShift = 9, + SpvImageOperandsNonPrivateTexelShift = 10, + SpvImageOperandsNonPrivateTexelKHRShift = 10, + SpvImageOperandsVolatileTexelShift = 11, + SpvImageOperandsVolatileTexelKHRShift = 11, + SpvImageOperandsSignExtendShift = 12, + SpvImageOperandsZeroExtendShift = 13, + SpvImageOperandsNontemporalShift = 14, + SpvImageOperandsOffsetsShift = 16, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, + SpvImageOperandsMakeTexelAvailableMask = 0x00000100, + SpvImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + SpvImageOperandsMakeTexelVisibleMask = 0x00000200, + SpvImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + SpvImageOperandsNonPrivateTexelMask = 0x00000400, + SpvImageOperandsNonPrivateTexelKHRMask = 0x00000400, + SpvImageOperandsVolatileTexelMask = 0x00000800, + SpvImageOperandsVolatileTexelKHRMask = 0x00000800, + SpvImageOperandsSignExtendMask = 0x00001000, + SpvImageOperandsZeroExtendMask = 0x00002000, + SpvImageOperandsNontemporalMask = 0x00004000, + SpvImageOperandsOffsetsMask = 0x00010000, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeAllowContractShift = 16, + SpvFPFastMathModeAllowContractFastINTELShift = 16, + SpvFPFastMathModeAllowReassocShift = 17, + SpvFPFastMathModeAllowReassocINTELShift = 17, + SpvFPFastMathModeAllowTransformShift = 18, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, + SpvFPFastMathModeAllowContractMask = 0x00010000, + SpvFPFastMathModeAllowContractFastINTELMask = 0x00010000, + SpvFPFastMathModeAllowReassocMask = 0x00020000, + SpvFPFastMathModeAllowReassocINTELMask = 0x00020000, + SpvFPFastMathModeAllowTransformMask = 0x00040000, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeLinkOnceODR = 2, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeRuntimeAlignedINTEL = 5940, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationUniformId = 27, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationMaxByteOffset = 45, + SpvDecorationAlignmentId = 46, + SpvDecorationMaxByteOffsetId = 47, + SpvDecorationNoSignedWrap = 4469, + SpvDecorationNoUnsignedWrap = 4470, + SpvDecorationWeightTextureQCOM = 4487, + SpvDecorationBlockMatchTextureQCOM = 4488, + SpvDecorationBlockMatchSamplerQCOM = 4499, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationNodeSharesPayloadLimitsWithAMDX = 5019, + SpvDecorationNodeMaxPayloadsAMDX = 5020, + SpvDecorationTrackFinishWritingAMDX = 5078, + SpvDecorationPayloadNodeNameAMDX = 5091, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationPerPrimitiveEXT = 5271, + SpvDecorationPerPrimitiveNV = 5271, + SpvDecorationPerViewNV = 5272, + SpvDecorationPerTaskNV = 5273, + SpvDecorationPerVertexKHR = 5285, + SpvDecorationPerVertexNV = 5285, + SpvDecorationNonUniform = 5300, + SpvDecorationNonUniformEXT = 5300, + SpvDecorationRestrictPointer = 5355, + SpvDecorationRestrictPointerEXT = 5355, + SpvDecorationAliasedPointer = 5356, + SpvDecorationAliasedPointerEXT = 5356, + SpvDecorationHitObjectShaderRecordBufferNV = 5386, + SpvDecorationBindlessSamplerNV = 5398, + SpvDecorationBindlessImageNV = 5399, + SpvDecorationBoundSamplerNV = 5400, + SpvDecorationBoundImageNV = 5401, + SpvDecorationSIMTCallINTEL = 5599, + SpvDecorationReferencedIndirectlyINTEL = 5602, + SpvDecorationClobberINTEL = 5607, + SpvDecorationSideEffectsINTEL = 5608, + SpvDecorationVectorComputeVariableINTEL = 5624, + SpvDecorationFuncParamIOKindINTEL = 5625, + SpvDecorationVectorComputeFunctionINTEL = 5626, + SpvDecorationStackCallINTEL = 5627, + SpvDecorationGlobalVariableOffsetINTEL = 5628, + SpvDecorationCounterBuffer = 5634, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationUserSemantic = 5635, + SpvDecorationUserTypeGOOGLE = 5636, + SpvDecorationFunctionRoundingModeINTEL = 5822, + SpvDecorationFunctionDenormModeINTEL = 5823, + SpvDecorationRegisterINTEL = 5825, + SpvDecorationMemoryINTEL = 5826, + SpvDecorationNumbanksINTEL = 5827, + SpvDecorationBankwidthINTEL = 5828, + SpvDecorationMaxPrivateCopiesINTEL = 5829, + SpvDecorationSinglepumpINTEL = 5830, + SpvDecorationDoublepumpINTEL = 5831, + SpvDecorationMaxReplicatesINTEL = 5832, + SpvDecorationSimpleDualPortINTEL = 5833, + SpvDecorationMergeINTEL = 5834, + SpvDecorationBankBitsINTEL = 5835, + SpvDecorationForcePow2DepthINTEL = 5836, + SpvDecorationStridesizeINTEL = 5883, + SpvDecorationWordsizeINTEL = 5884, + SpvDecorationTrueDualPortINTEL = 5885, + SpvDecorationBurstCoalesceINTEL = 5899, + SpvDecorationCacheSizeINTEL = 5900, + SpvDecorationDontStaticallyCoalesceINTEL = 5901, + SpvDecorationPrefetchINTEL = 5902, + SpvDecorationStallEnableINTEL = 5905, + SpvDecorationFuseLoopsInFunctionINTEL = 5907, + SpvDecorationMathOpDSPModeINTEL = 5909, + SpvDecorationAliasScopeINTEL = 5914, + SpvDecorationNoAliasINTEL = 5915, + SpvDecorationInitiationIntervalINTEL = 5917, + SpvDecorationMaxConcurrencyINTEL = 5918, + SpvDecorationPipelineEnableINTEL = 5919, + SpvDecorationBufferLocationINTEL = 5921, + SpvDecorationIOPipeStorageINTEL = 5944, + SpvDecorationFunctionFloatingPointModeINTEL = 6080, + SpvDecorationSingleElementVectorINTEL = 6085, + SpvDecorationVectorComputeCallableFunctionINTEL = 6087, + SpvDecorationMediaBlockIOINTEL = 6140, + SpvDecorationStallFreeINTEL = 6151, + SpvDecorationFPMaxErrorDecorationINTEL = 6170, + SpvDecorationLatencyControlLabelINTEL = 6172, + SpvDecorationLatencyControlConstraintINTEL = 6173, + SpvDecorationConduitKernelArgumentINTEL = 6175, + SpvDecorationRegisterMapKernelArgumentINTEL = 6176, + SpvDecorationMMHostInterfaceAddressWidthINTEL = 6177, + SpvDecorationMMHostInterfaceDataWidthINTEL = 6178, + SpvDecorationMMHostInterfaceLatencyINTEL = 6179, + SpvDecorationMMHostInterfaceReadWriteModeINTEL = 6180, + SpvDecorationMMHostInterfaceMaxBurstINTEL = 6181, + SpvDecorationMMHostInterfaceWaitRequestINTEL = 6182, + SpvDecorationStableKernelArgumentINTEL = 6183, + SpvDecorationHostAccessINTEL = 6188, + SpvDecorationInitModeINTEL = 6190, + SpvDecorationImplementInRegisterMapINTEL = 6191, + SpvDecorationCacheControlLoadINTEL = 6442, + SpvDecorationCacheControlStoreINTEL = 6443, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInCoreIDARM = 4160, + SpvBuiltInCoreCountARM = 4161, + SpvBuiltInCoreMaxIDARM = 4162, + SpvBuiltInWarpIDARM = 4163, + SpvBuiltInWarpMaxIDARM = 4164, + SpvBuiltInSubgroupEqMask = 4416, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMask = 4417, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMask = 4418, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMask = 4419, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMask = 4420, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInPrimitiveShadingRateKHR = 4432, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInShadingRateKHR = 4444, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInCoalescedInputCountAMDX = 5021, + SpvBuiltInShaderIndexAMDX = 5073, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInFullyCoveredEXT = 5264, + SpvBuiltInTaskCountNV = 5274, + SpvBuiltInPrimitiveCountNV = 5275, + SpvBuiltInPrimitiveIndicesNV = 5276, + SpvBuiltInClipDistancePerViewNV = 5277, + SpvBuiltInCullDistancePerViewNV = 5278, + SpvBuiltInLayerPerViewNV = 5279, + SpvBuiltInMeshViewCountNV = 5280, + SpvBuiltInMeshViewIndicesNV = 5281, + SpvBuiltInBaryCoordKHR = 5286, + SpvBuiltInBaryCoordNV = 5286, + SpvBuiltInBaryCoordNoPerspKHR = 5287, + SpvBuiltInBaryCoordNoPerspNV = 5287, + SpvBuiltInFragSizeEXT = 5292, + SpvBuiltInFragmentSizeNV = 5292, + SpvBuiltInFragInvocationCountEXT = 5293, + SpvBuiltInInvocationsPerPixelNV = 5293, + SpvBuiltInPrimitivePointIndicesEXT = 5294, + SpvBuiltInPrimitiveLineIndicesEXT = 5295, + SpvBuiltInPrimitiveTriangleIndicesEXT = 5296, + SpvBuiltInCullPrimitiveEXT = 5299, + SpvBuiltInLaunchIdKHR = 5319, + SpvBuiltInLaunchIdNV = 5319, + SpvBuiltInLaunchSizeKHR = 5320, + SpvBuiltInLaunchSizeNV = 5320, + SpvBuiltInWorldRayOriginKHR = 5321, + SpvBuiltInWorldRayOriginNV = 5321, + SpvBuiltInWorldRayDirectionKHR = 5322, + SpvBuiltInWorldRayDirectionNV = 5322, + SpvBuiltInObjectRayOriginKHR = 5323, + SpvBuiltInObjectRayOriginNV = 5323, + SpvBuiltInObjectRayDirectionKHR = 5324, + SpvBuiltInObjectRayDirectionNV = 5324, + SpvBuiltInRayTminKHR = 5325, + SpvBuiltInRayTminNV = 5325, + SpvBuiltInRayTmaxKHR = 5326, + SpvBuiltInRayTmaxNV = 5326, + SpvBuiltInInstanceCustomIndexKHR = 5327, + SpvBuiltInInstanceCustomIndexNV = 5327, + SpvBuiltInObjectToWorldKHR = 5330, + SpvBuiltInObjectToWorldNV = 5330, + SpvBuiltInWorldToObjectKHR = 5331, + SpvBuiltInWorldToObjectNV = 5331, + SpvBuiltInHitTNV = 5332, + SpvBuiltInHitKindKHR = 5333, + SpvBuiltInHitKindNV = 5333, + SpvBuiltInCurrentRayTimeNV = 5334, + SpvBuiltInHitTriangleVertexPositionsKHR = 5335, + SpvBuiltInHitMicroTriangleVertexPositionsNV = 5337, + SpvBuiltInHitMicroTriangleVertexBarycentricsNV = 5344, + SpvBuiltInIncomingRayFlagsKHR = 5351, + SpvBuiltInIncomingRayFlagsNV = 5351, + SpvBuiltInRayGeometryIndexKHR = 5352, + SpvBuiltInWarpsPerSMNV = 5374, + SpvBuiltInSMCountNV = 5375, + SpvBuiltInWarpIDNV = 5376, + SpvBuiltInSMIDNV = 5377, + SpvBuiltInHitKindFrontFacingMicroTriangleNV = 5405, + SpvBuiltInHitKindBackFacingMicroTriangleNV = 5406, + SpvBuiltInCullMaskKHR = 6021, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlDependencyInfiniteShift = 2, + SpvLoopControlDependencyLengthShift = 3, + SpvLoopControlMinIterationsShift = 4, + SpvLoopControlMaxIterationsShift = 5, + SpvLoopControlIterationMultipleShift = 6, + SpvLoopControlPeelCountShift = 7, + SpvLoopControlPartialCountShift = 8, + SpvLoopControlInitiationIntervalINTELShift = 16, + SpvLoopControlMaxConcurrencyINTELShift = 17, + SpvLoopControlDependencyArrayINTELShift = 18, + SpvLoopControlPipelineEnableINTELShift = 19, + SpvLoopControlLoopCoalesceINTELShift = 20, + SpvLoopControlMaxInterleavingINTELShift = 21, + SpvLoopControlSpeculatedIterationsINTELShift = 22, + SpvLoopControlNoFusionINTELShift = 23, + SpvLoopControlLoopCountINTELShift = 24, + SpvLoopControlMaxReinvocationDelayINTELShift = 25, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, + SpvLoopControlDependencyInfiniteMask = 0x00000004, + SpvLoopControlDependencyLengthMask = 0x00000008, + SpvLoopControlMinIterationsMask = 0x00000010, + SpvLoopControlMaxIterationsMask = 0x00000020, + SpvLoopControlIterationMultipleMask = 0x00000040, + SpvLoopControlPeelCountMask = 0x00000080, + SpvLoopControlPartialCountMask = 0x00000100, + SpvLoopControlInitiationIntervalINTELMask = 0x00010000, + SpvLoopControlMaxConcurrencyINTELMask = 0x00020000, + SpvLoopControlDependencyArrayINTELMask = 0x00040000, + SpvLoopControlPipelineEnableINTELMask = 0x00080000, + SpvLoopControlLoopCoalesceINTELMask = 0x00100000, + SpvLoopControlMaxInterleavingINTELMask = 0x00200000, + SpvLoopControlSpeculatedIterationsINTELMask = 0x00400000, + SpvLoopControlNoFusionINTELMask = 0x00800000, + SpvLoopControlLoopCountINTELMask = 0x01000000, + SpvLoopControlMaxReinvocationDelayINTELMask = 0x02000000, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlOptNoneINTELShift = 16, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, + SpvFunctionControlOptNoneINTELMask = 0x00010000, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsOutputMemoryShift = 12, + SpvMemorySemanticsOutputMemoryKHRShift = 12, + SpvMemorySemanticsMakeAvailableShift = 13, + SpvMemorySemanticsMakeAvailableKHRShift = 13, + SpvMemorySemanticsMakeVisibleShift = 14, + SpvMemorySemanticsMakeVisibleKHRShift = 14, + SpvMemorySemanticsVolatileShift = 15, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, + SpvMemorySemanticsOutputMemoryMask = 0x00001000, + SpvMemorySemanticsOutputMemoryKHRMask = 0x00001000, + SpvMemorySemanticsMakeAvailableMask = 0x00002000, + SpvMemorySemanticsMakeAvailableKHRMask = 0x00002000, + SpvMemorySemanticsMakeVisibleMask = 0x00004000, + SpvMemorySemanticsMakeVisibleKHRMask = 0x00004000, + SpvMemorySemanticsVolatileMask = 0x00008000, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMakePointerAvailableShift = 3, + SpvMemoryAccessMakePointerAvailableKHRShift = 3, + SpvMemoryAccessMakePointerVisibleShift = 4, + SpvMemoryAccessMakePointerVisibleKHRShift = 4, + SpvMemoryAccessNonPrivatePointerShift = 5, + SpvMemoryAccessNonPrivatePointerKHRShift = 5, + SpvMemoryAccessAliasScopeINTELMaskShift = 16, + SpvMemoryAccessNoAliasINTELMaskShift = 17, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, + SpvMemoryAccessMakePointerAvailableMask = 0x00000008, + SpvMemoryAccessMakePointerAvailableKHRMask = 0x00000008, + SpvMemoryAccessMakePointerVisibleMask = 0x00000010, + SpvMemoryAccessMakePointerVisibleKHRMask = 0x00000010, + SpvMemoryAccessNonPrivatePointerMask = 0x00000020, + SpvMemoryAccessNonPrivatePointerKHRMask = 0x00000020, + SpvMemoryAccessAliasScopeINTELMaskMask = 0x00010000, + SpvMemoryAccessNoAliasINTELMaskMask = 0x00020000, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeQueueFamily = 5, + SpvScopeQueueFamilyKHR = 5, + SpvScopeShaderCallKHR = 6, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationClusteredReduce = 3, + SpvGroupOperationPartitionedReduceNV = 6, + SpvGroupOperationPartitionedInclusiveScanNV = 7, + SpvGroupOperationPartitionedExclusiveScanNV = 8, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupDispatch = 58, + SpvCapabilityNamedBarrier = 59, + SpvCapabilityPipeStorage = 60, + SpvCapabilityGroupNonUniform = 61, + SpvCapabilityGroupNonUniformVote = 62, + SpvCapabilityGroupNonUniformArithmetic = 63, + SpvCapabilityGroupNonUniformBallot = 64, + SpvCapabilityGroupNonUniformShuffle = 65, + SpvCapabilityGroupNonUniformShuffleRelative = 66, + SpvCapabilityGroupNonUniformClustered = 67, + SpvCapabilityGroupNonUniformQuad = 68, + SpvCapabilityShaderLayer = 69, + SpvCapabilityShaderViewportIndex = 70, + SpvCapabilityUniformDecoration = 71, + SpvCapabilityCoreBuiltinsARM = 4165, + SpvCapabilityTileImageColorReadAccessEXT = 4166, + SpvCapabilityTileImageDepthReadAccessEXT = 4167, + SpvCapabilityTileImageStencilReadAccessEXT = 4168, + SpvCapabilityFragmentShadingRateKHR = 4422, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilityWorkgroupMemoryExplicitLayoutKHR = 4428, + SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityStorageBuffer8BitAccess = 4448, + SpvCapabilityUniformAndStorageBuffer8BitAccess = 4449, + SpvCapabilityStoragePushConstant8 = 4450, + SpvCapabilityDenormPreserve = 4464, + SpvCapabilityDenormFlushToZero = 4465, + SpvCapabilitySignedZeroInfNanPreserve = 4466, + SpvCapabilityRoundingModeRTE = 4467, + SpvCapabilityRoundingModeRTZ = 4468, + SpvCapabilityRayQueryProvisionalKHR = 4471, + SpvCapabilityRayQueryKHR = 4472, + SpvCapabilityRayTraversalPrimitiveCullingKHR = 4478, + SpvCapabilityRayTracingKHR = 4479, + SpvCapabilityTextureSampleWeightedQCOM = 4484, + SpvCapabilityTextureBoxFilterQCOM = 4485, + SpvCapabilityTextureBlockMatchQCOM = 4486, + SpvCapabilityTextureBlockMatch2QCOM = 4498, + SpvCapabilityFloat16ImageAMD = 5008, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilityInt64ImageEXT = 5016, + SpvCapabilityShaderClockKHR = 5055, + SpvCapabilityShaderEnqueueAMDX = 5067, + SpvCapabilityQuadControlKHR = 5087, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilityFragmentFullyCoveredEXT = 5265, + SpvCapabilityMeshShadingNV = 5266, + SpvCapabilityImageFootprintNV = 5282, + SpvCapabilityMeshShadingEXT = 5283, + SpvCapabilityFragmentBarycentricKHR = 5284, + SpvCapabilityFragmentBarycentricNV = 5284, + SpvCapabilityComputeDerivativeGroupQuadsNV = 5288, + SpvCapabilityFragmentDensityEXT = 5291, + SpvCapabilityShadingRateNV = 5291, + SpvCapabilityGroupNonUniformPartitionedNV = 5297, + SpvCapabilityShaderNonUniform = 5301, + SpvCapabilityShaderNonUniformEXT = 5301, + SpvCapabilityRuntimeDescriptorArray = 5302, + SpvCapabilityRuntimeDescriptorArrayEXT = 5302, + SpvCapabilityInputAttachmentArrayDynamicIndexing = 5303, + SpvCapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + SpvCapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + SpvCapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + SpvCapabilityUniformBufferArrayNonUniformIndexing = 5306, + SpvCapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + SpvCapabilitySampledImageArrayNonUniformIndexing = 5307, + SpvCapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + SpvCapabilityStorageBufferArrayNonUniformIndexing = 5308, + SpvCapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + SpvCapabilityStorageImageArrayNonUniformIndexing = 5309, + SpvCapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + SpvCapabilityInputAttachmentArrayNonUniformIndexing = 5310, + SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + SpvCapabilityRayTracingPositionFetchKHR = 5336, + SpvCapabilityRayTracingNV = 5340, + SpvCapabilityRayTracingMotionBlurNV = 5341, + SpvCapabilityVulkanMemoryModel = 5345, + SpvCapabilityVulkanMemoryModelKHR = 5345, + SpvCapabilityVulkanMemoryModelDeviceScope = 5346, + SpvCapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + SpvCapabilityPhysicalStorageBufferAddresses = 5347, + SpvCapabilityPhysicalStorageBufferAddressesEXT = 5347, + SpvCapabilityComputeDerivativeGroupLinearNV = 5350, + SpvCapabilityRayTracingProvisionalKHR = 5353, + SpvCapabilityCooperativeMatrixNV = 5357, + SpvCapabilityFragmentShaderSampleInterlockEXT = 5363, + SpvCapabilityFragmentShaderShadingRateInterlockEXT = 5372, + SpvCapabilityShaderSMBuiltinsNV = 5373, + SpvCapabilityFragmentShaderPixelInterlockEXT = 5378, + SpvCapabilityDemoteToHelperInvocation = 5379, + SpvCapabilityDemoteToHelperInvocationEXT = 5379, + SpvCapabilityDisplacementMicromapNV = 5380, + SpvCapabilityRayTracingOpacityMicromapEXT = 5381, + SpvCapabilityShaderInvocationReorderNV = 5383, + SpvCapabilityBindlessTextureNV = 5390, + SpvCapabilityRayQueryPositionFetchKHR = 5391, + SpvCapabilityAtomicFloat16VectorNV = 5404, + SpvCapabilityRayTracingDisplacementMicromapNV = 5409, + SpvCapabilityRawAccessChainsNV = 5414, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilitySubgroupImageMediaBlockIOINTEL = 5579, + SpvCapabilityRoundToInfinityINTEL = 5582, + SpvCapabilityFloatingPointModeINTEL = 5583, + SpvCapabilityIntegerFunctions2INTEL = 5584, + SpvCapabilityFunctionPointersINTEL = 5603, + SpvCapabilityIndirectReferencesINTEL = 5604, + SpvCapabilityAsmINTEL = 5606, + SpvCapabilityAtomicFloat32MinMaxEXT = 5612, + SpvCapabilityAtomicFloat64MinMaxEXT = 5613, + SpvCapabilityAtomicFloat16MinMaxEXT = 5616, + SpvCapabilityVectorComputeINTEL = 5617, + SpvCapabilityVectorAnyINTEL = 5619, + SpvCapabilityExpectAssumeKHR = 5629, + SpvCapabilitySubgroupAvcMotionEstimationINTEL = 5696, + SpvCapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + SpvCapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + SpvCapabilityVariableLengthArrayINTEL = 5817, + SpvCapabilityFunctionFloatControlINTEL = 5821, + SpvCapabilityFPGAMemoryAttributesINTEL = 5824, + SpvCapabilityFPFastMathModeINTEL = 5837, + SpvCapabilityArbitraryPrecisionIntegersINTEL = 5844, + SpvCapabilityArbitraryPrecisionFloatingPointINTEL = 5845, + SpvCapabilityUnstructuredLoopControlsINTEL = 5886, + SpvCapabilityFPGALoopControlsINTEL = 5888, + SpvCapabilityKernelAttributesINTEL = 5892, + SpvCapabilityFPGAKernelAttributesINTEL = 5897, + SpvCapabilityFPGAMemoryAccessesINTEL = 5898, + SpvCapabilityFPGAClusterAttributesINTEL = 5904, + SpvCapabilityLoopFuseINTEL = 5906, + SpvCapabilityFPGADSPControlINTEL = 5908, + SpvCapabilityMemoryAccessAliasingINTEL = 5910, + SpvCapabilityFPGAInvocationPipeliningAttributesINTEL = 5916, + SpvCapabilityFPGABufferLocationINTEL = 5920, + SpvCapabilityArbitraryPrecisionFixedPointINTEL = 5922, + SpvCapabilityUSMStorageClassesINTEL = 5935, + SpvCapabilityRuntimeAlignedAttributeINTEL = 5939, + SpvCapabilityIOPipesINTEL = 5943, + SpvCapabilityBlockingPipesINTEL = 5945, + SpvCapabilityFPGARegINTEL = 5948, + SpvCapabilityDotProductInputAll = 6016, + SpvCapabilityDotProductInputAllKHR = 6016, + SpvCapabilityDotProductInput4x8Bit = 6017, + SpvCapabilityDotProductInput4x8BitKHR = 6017, + SpvCapabilityDotProductInput4x8BitPacked = 6018, + SpvCapabilityDotProductInput4x8BitPackedKHR = 6018, + SpvCapabilityDotProduct = 6019, + SpvCapabilityDotProductKHR = 6019, + SpvCapabilityRayCullMaskKHR = 6020, + SpvCapabilityCooperativeMatrixKHR = 6022, + SpvCapabilityBitInstructions = 6025, + SpvCapabilityGroupNonUniformRotateKHR = 6026, + SpvCapabilityFloatControls2 = 6029, + SpvCapabilityAtomicFloat32AddEXT = 6033, + SpvCapabilityAtomicFloat64AddEXT = 6034, + SpvCapabilityLongCompositesINTEL = 6089, + SpvCapabilityOptNoneINTEL = 6094, + SpvCapabilityAtomicFloat16AddEXT = 6095, + SpvCapabilityDebugInfoModuleINTEL = 6114, + SpvCapabilityBFloat16ConversionINTEL = 6115, + SpvCapabilitySplitBarrierINTEL = 6141, + SpvCapabilityFPGAClusterAttributesV2INTEL = 6150, + SpvCapabilityFPGAKernelAttributesv2INTEL = 6161, + SpvCapabilityFPMaxErrorINTEL = 6169, + SpvCapabilityFPGALatencyControlINTEL = 6171, + SpvCapabilityFPGAArgumentInterfacesINTEL = 6174, + SpvCapabilityGlobalVariableHostAccessINTEL = 6187, + SpvCapabilityGlobalVariableFPGADecorationsINTEL = 6189, + SpvCapabilityGroupUniformArithmeticKHR = 6400, + SpvCapabilityMaskedGatherScatterINTEL = 6427, + SpvCapabilityCacheControlsINTEL = 6441, + SpvCapabilityRegisterLimitsINTEL = 6460, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvRayFlagsShift_ { + SpvRayFlagsOpaqueKHRShift = 0, + SpvRayFlagsNoOpaqueKHRShift = 1, + SpvRayFlagsTerminateOnFirstHitKHRShift = 2, + SpvRayFlagsSkipClosestHitShaderKHRShift = 3, + SpvRayFlagsCullBackFacingTrianglesKHRShift = 4, + SpvRayFlagsCullFrontFacingTrianglesKHRShift = 5, + SpvRayFlagsCullOpaqueKHRShift = 6, + SpvRayFlagsCullNoOpaqueKHRShift = 7, + SpvRayFlagsSkipTrianglesKHRShift = 8, + SpvRayFlagsSkipAABBsKHRShift = 9, + SpvRayFlagsForceOpacityMicromap2StateEXTShift = 10, + SpvRayFlagsMax = 0x7fffffff, +} SpvRayFlagsShift; + +typedef enum SpvRayFlagsMask_ { + SpvRayFlagsMaskNone = 0, + SpvRayFlagsOpaqueKHRMask = 0x00000001, + SpvRayFlagsNoOpaqueKHRMask = 0x00000002, + SpvRayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + SpvRayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + SpvRayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + SpvRayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + SpvRayFlagsCullOpaqueKHRMask = 0x00000040, + SpvRayFlagsCullNoOpaqueKHRMask = 0x00000080, + SpvRayFlagsSkipTrianglesKHRMask = 0x00000100, + SpvRayFlagsSkipAABBsKHRMask = 0x00000200, + SpvRayFlagsForceOpacityMicromap2StateEXTMask = 0x00000400, +} SpvRayFlagsMask; + +typedef enum SpvRayQueryIntersection_ { + SpvRayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + SpvRayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + SpvRayQueryIntersectionMax = 0x7fffffff, +} SpvRayQueryIntersection; + +typedef enum SpvRayQueryCommittedIntersectionType_ { + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + SpvRayQueryCommittedIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCommittedIntersectionType; + +typedef enum SpvRayQueryCandidateIntersectionType_ { + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + SpvRayQueryCandidateIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCandidateIntersectionType; + +typedef enum SpvFragmentShadingRateShift_ { + SpvFragmentShadingRateVertical2PixelsShift = 0, + SpvFragmentShadingRateVertical4PixelsShift = 1, + SpvFragmentShadingRateHorizontal2PixelsShift = 2, + SpvFragmentShadingRateHorizontal4PixelsShift = 3, + SpvFragmentShadingRateMax = 0x7fffffff, +} SpvFragmentShadingRateShift; + +typedef enum SpvFragmentShadingRateMask_ { + SpvFragmentShadingRateMaskNone = 0, + SpvFragmentShadingRateVertical2PixelsMask = 0x00000001, + SpvFragmentShadingRateVertical4PixelsMask = 0x00000002, + SpvFragmentShadingRateHorizontal2PixelsMask = 0x00000004, + SpvFragmentShadingRateHorizontal4PixelsMask = 0x00000008, +} SpvFragmentShadingRateMask; + +typedef enum SpvFPDenormMode_ { + SpvFPDenormModePreserve = 0, + SpvFPDenormModeFlushToZero = 1, + SpvFPDenormModeMax = 0x7fffffff, +} SpvFPDenormMode; + +typedef enum SpvFPOperationMode_ { + SpvFPOperationModeIEEE = 0, + SpvFPOperationModeALT = 1, + SpvFPOperationModeMax = 0x7fffffff, +} SpvFPOperationMode; + +typedef enum SpvQuantizationModes_ { + SpvQuantizationModesTRN = 0, + SpvQuantizationModesTRN_ZERO = 1, + SpvQuantizationModesRND = 2, + SpvQuantizationModesRND_ZERO = 3, + SpvQuantizationModesRND_INF = 4, + SpvQuantizationModesRND_MIN_INF = 5, + SpvQuantizationModesRND_CONV = 6, + SpvQuantizationModesRND_CONV_ODD = 7, + SpvQuantizationModesMax = 0x7fffffff, +} SpvQuantizationModes; + +typedef enum SpvOverflowModes_ { + SpvOverflowModesWRAP = 0, + SpvOverflowModesSAT = 1, + SpvOverflowModesSAT_ZERO = 2, + SpvOverflowModesSAT_SYM = 3, + SpvOverflowModesMax = 0x7fffffff, +} SpvOverflowModes; + +typedef enum SpvPackedVectorFormat_ { + SpvPackedVectorFormatPackedVectorFormat4x8Bit = 0, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR = 0, + SpvPackedVectorFormatMax = 0x7fffffff, +} SpvPackedVectorFormat; + +typedef enum SpvCooperativeMatrixOperandsShift_ { + SpvCooperativeMatrixOperandsMatrixASignedComponentsKHRShift = 0, + SpvCooperativeMatrixOperandsMatrixBSignedComponentsKHRShift = 1, + SpvCooperativeMatrixOperandsMatrixCSignedComponentsKHRShift = 2, + SpvCooperativeMatrixOperandsMatrixResultSignedComponentsKHRShift = 3, + SpvCooperativeMatrixOperandsSaturatingAccumulationKHRShift = 4, + SpvCooperativeMatrixOperandsMax = 0x7fffffff, +} SpvCooperativeMatrixOperandsShift; + +typedef enum SpvCooperativeMatrixOperandsMask_ { + SpvCooperativeMatrixOperandsMaskNone = 0, + SpvCooperativeMatrixOperandsMatrixASignedComponentsKHRMask = 0x00000001, + SpvCooperativeMatrixOperandsMatrixBSignedComponentsKHRMask = 0x00000002, + SpvCooperativeMatrixOperandsMatrixCSignedComponentsKHRMask = 0x00000004, + SpvCooperativeMatrixOperandsMatrixResultSignedComponentsKHRMask = 0x00000008, + SpvCooperativeMatrixOperandsSaturatingAccumulationKHRMask = 0x00000010, +} SpvCooperativeMatrixOperandsMask; + +typedef enum SpvCooperativeMatrixLayout_ { + SpvCooperativeMatrixLayoutRowMajorKHR = 0, + SpvCooperativeMatrixLayoutColumnMajorKHR = 1, + SpvCooperativeMatrixLayoutMax = 0x7fffffff, +} SpvCooperativeMatrixLayout; + +typedef enum SpvCooperativeMatrixUse_ { + SpvCooperativeMatrixUseMatrixAKHR = 0, + SpvCooperativeMatrixUseMatrixBKHR = 1, + SpvCooperativeMatrixUseMatrixAccumulatorKHR = 2, + SpvCooperativeMatrixUseMax = 0x7fffffff, +} SpvCooperativeMatrixUse; + +typedef enum SpvInitializationModeQualifier_ { + SpvInitializationModeQualifierInitOnDeviceReprogramINTEL = 0, + SpvInitializationModeQualifierInitOnDeviceResetINTEL = 1, + SpvInitializationModeQualifierMax = 0x7fffffff, +} SpvInitializationModeQualifier; + +typedef enum SpvHostAccessQualifier_ { + SpvHostAccessQualifierNoneINTEL = 0, + SpvHostAccessQualifierReadINTEL = 1, + SpvHostAccessQualifierWriteINTEL = 2, + SpvHostAccessQualifierReadWriteINTEL = 3, + SpvHostAccessQualifierMax = 0x7fffffff, +} SpvHostAccessQualifier; + +typedef enum SpvLoadCacheControl_ { + SpvLoadCacheControlUncachedINTEL = 0, + SpvLoadCacheControlCachedINTEL = 1, + SpvLoadCacheControlStreamingINTEL = 2, + SpvLoadCacheControlInvalidateAfterReadINTEL = 3, + SpvLoadCacheControlConstCachedINTEL = 4, + SpvLoadCacheControlMax = 0x7fffffff, +} SpvLoadCacheControl; + +typedef enum SpvStoreCacheControl_ { + SpvStoreCacheControlUncachedINTEL = 0, + SpvStoreCacheControlWriteThroughINTEL = 1, + SpvStoreCacheControlWriteBackINTEL = 2, + SpvStoreCacheControlStreamingINTEL = 3, + SpvStoreCacheControlMax = 0x7fffffff, +} SpvStoreCacheControl; + +typedef enum SpvNamedMaximumNumberOfRegisters_ { + SpvNamedMaximumNumberOfRegistersAutoINTEL = 0, + SpvNamedMaximumNumberOfRegistersMax = 0x7fffffff, +} SpvNamedMaximumNumberOfRegisters; + +typedef enum SpvRawAccessChainOperandsShift_ { + SpvRawAccessChainOperandsRobustnessPerComponentNVShift = 0, + SpvRawAccessChainOperandsRobustnessPerElementNVShift = 1, + SpvRawAccessChainOperandsMax = 0x7fffffff, +} SpvRawAccessChainOperandsShift; + +typedef enum SpvRawAccessChainOperandsMask_ { + SpvRawAccessChainOperandsMaskNone = 0, + SpvRawAccessChainOperandsRobustnessPerComponentNVMask = 0x00000001, + SpvRawAccessChainOperandsRobustnessPerElementNVMask = 0x00000002, +} SpvRawAccessChainOperandsMask; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpSizeOf = 321, + SpvOpTypePipeStorage = 322, + SpvOpConstantPipeStorage = 323, + SpvOpCreatePipeFromPipeStorage = 324, + SpvOpGetKernelLocalSizeForSubgroupCount = 325, + SpvOpGetKernelMaxNumSubgroups = 326, + SpvOpTypeNamedBarrier = 327, + SpvOpNamedBarrierInitialize = 328, + SpvOpMemoryNamedBarrier = 329, + SpvOpModuleProcessed = 330, + SpvOpExecutionModeId = 331, + SpvOpDecorateId = 332, + SpvOpGroupNonUniformElect = 333, + SpvOpGroupNonUniformAll = 334, + SpvOpGroupNonUniformAny = 335, + SpvOpGroupNonUniformAllEqual = 336, + SpvOpGroupNonUniformBroadcast = 337, + SpvOpGroupNonUniformBroadcastFirst = 338, + SpvOpGroupNonUniformBallot = 339, + SpvOpGroupNonUniformInverseBallot = 340, + SpvOpGroupNonUniformBallotBitExtract = 341, + SpvOpGroupNonUniformBallotBitCount = 342, + SpvOpGroupNonUniformBallotFindLSB = 343, + SpvOpGroupNonUniformBallotFindMSB = 344, + SpvOpGroupNonUniformShuffle = 345, + SpvOpGroupNonUniformShuffleXor = 346, + SpvOpGroupNonUniformShuffleUp = 347, + SpvOpGroupNonUniformShuffleDown = 348, + SpvOpGroupNonUniformIAdd = 349, + SpvOpGroupNonUniformFAdd = 350, + SpvOpGroupNonUniformIMul = 351, + SpvOpGroupNonUniformFMul = 352, + SpvOpGroupNonUniformSMin = 353, + SpvOpGroupNonUniformUMin = 354, + SpvOpGroupNonUniformFMin = 355, + SpvOpGroupNonUniformSMax = 356, + SpvOpGroupNonUniformUMax = 357, + SpvOpGroupNonUniformFMax = 358, + SpvOpGroupNonUniformBitwiseAnd = 359, + SpvOpGroupNonUniformBitwiseOr = 360, + SpvOpGroupNonUniformBitwiseXor = 361, + SpvOpGroupNonUniformLogicalAnd = 362, + SpvOpGroupNonUniformLogicalOr = 363, + SpvOpGroupNonUniformLogicalXor = 364, + SpvOpGroupNonUniformQuadBroadcast = 365, + SpvOpGroupNonUniformQuadSwap = 366, + SpvOpCopyLogical = 400, + SpvOpPtrEqual = 401, + SpvOpPtrNotEqual = 402, + SpvOpPtrDiff = 403, + SpvOpColorAttachmentReadEXT = 4160, + SpvOpDepthAttachmentReadEXT = 4161, + SpvOpStencilAttachmentReadEXT = 4162, + SpvOpTerminateInvocation = 4416, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpGroupNonUniformRotateKHR = 4431, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpTraceRayKHR = 4445, + SpvOpExecuteCallableKHR = 4446, + SpvOpConvertUToAccelerationStructureKHR = 4447, + SpvOpIgnoreIntersectionKHR = 4448, + SpvOpTerminateRayKHR = 4449, + SpvOpSDot = 4450, + SpvOpSDotKHR = 4450, + SpvOpUDot = 4451, + SpvOpUDotKHR = 4451, + SpvOpSUDot = 4452, + SpvOpSUDotKHR = 4452, + SpvOpSDotAccSat = 4453, + SpvOpSDotAccSatKHR = 4453, + SpvOpUDotAccSat = 4454, + SpvOpUDotAccSatKHR = 4454, + SpvOpSUDotAccSat = 4455, + SpvOpSUDotAccSatKHR = 4455, + SpvOpTypeCooperativeMatrixKHR = 4456, + SpvOpCooperativeMatrixLoadKHR = 4457, + SpvOpCooperativeMatrixStoreKHR = 4458, + SpvOpCooperativeMatrixMulAddKHR = 4459, + SpvOpCooperativeMatrixLengthKHR = 4460, + SpvOpTypeRayQueryKHR = 4472, + SpvOpRayQueryInitializeKHR = 4473, + SpvOpRayQueryTerminateKHR = 4474, + SpvOpRayQueryGenerateIntersectionKHR = 4475, + SpvOpRayQueryConfirmIntersectionKHR = 4476, + SpvOpRayQueryProceedKHR = 4477, + SpvOpRayQueryGetIntersectionTypeKHR = 4479, + SpvOpImageSampleWeightedQCOM = 4480, + SpvOpImageBoxFilterQCOM = 4481, + SpvOpImageBlockMatchSSDQCOM = 4482, + SpvOpImageBlockMatchSADQCOM = 4483, + SpvOpImageBlockMatchWindowSSDQCOM = 4500, + SpvOpImageBlockMatchWindowSADQCOM = 4501, + SpvOpImageBlockMatchGatherSSDQCOM = 4502, + SpvOpImageBlockMatchGatherSADQCOM = 4503, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpReadClockKHR = 5056, + SpvOpFinalizeNodePayloadsAMDX = 5075, + SpvOpFinishWritingNodePayloadAMDX = 5078, + SpvOpInitializeNodePayloadsAMDX = 5090, + SpvOpGroupNonUniformQuadAllKHR = 5110, + SpvOpGroupNonUniformQuadAnyKHR = 5111, + SpvOpHitObjectRecordHitMotionNV = 5249, + SpvOpHitObjectRecordHitWithIndexMotionNV = 5250, + SpvOpHitObjectRecordMissMotionNV = 5251, + SpvOpHitObjectGetWorldToObjectNV = 5252, + SpvOpHitObjectGetObjectToWorldNV = 5253, + SpvOpHitObjectGetObjectRayDirectionNV = 5254, + SpvOpHitObjectGetObjectRayOriginNV = 5255, + SpvOpHitObjectTraceRayMotionNV = 5256, + SpvOpHitObjectGetShaderRecordBufferHandleNV = 5257, + SpvOpHitObjectGetShaderBindingTableRecordIndexNV = 5258, + SpvOpHitObjectRecordEmptyNV = 5259, + SpvOpHitObjectTraceRayNV = 5260, + SpvOpHitObjectRecordHitNV = 5261, + SpvOpHitObjectRecordHitWithIndexNV = 5262, + SpvOpHitObjectRecordMissNV = 5263, + SpvOpHitObjectExecuteShaderNV = 5264, + SpvOpHitObjectGetCurrentTimeNV = 5265, + SpvOpHitObjectGetAttributesNV = 5266, + SpvOpHitObjectGetHitKindNV = 5267, + SpvOpHitObjectGetPrimitiveIndexNV = 5268, + SpvOpHitObjectGetGeometryIndexNV = 5269, + SpvOpHitObjectGetInstanceIdNV = 5270, + SpvOpHitObjectGetInstanceCustomIndexNV = 5271, + SpvOpHitObjectGetWorldRayDirectionNV = 5272, + SpvOpHitObjectGetWorldRayOriginNV = 5273, + SpvOpHitObjectGetRayTMaxNV = 5274, + SpvOpHitObjectGetRayTMinNV = 5275, + SpvOpHitObjectIsEmptyNV = 5276, + SpvOpHitObjectIsHitNV = 5277, + SpvOpHitObjectIsMissNV = 5278, + SpvOpReorderThreadWithHitObjectNV = 5279, + SpvOpReorderThreadWithHintNV = 5280, + SpvOpTypeHitObjectNV = 5281, + SpvOpImageSampleFootprintNV = 5283, + SpvOpEmitMeshTasksEXT = 5294, + SpvOpSetMeshOutputsEXT = 5295, + SpvOpGroupNonUniformPartitionNV = 5296, + SpvOpWritePackedPrimitiveIndices4x8NV = 5299, + SpvOpFetchMicroTriangleVertexPositionNV = 5300, + SpvOpFetchMicroTriangleVertexBarycentricNV = 5301, + SpvOpReportIntersectionKHR = 5334, + SpvOpReportIntersectionNV = 5334, + SpvOpIgnoreIntersectionNV = 5335, + SpvOpTerminateRayNV = 5336, + SpvOpTraceNV = 5337, + SpvOpTraceMotionNV = 5338, + SpvOpTraceRayMotionNV = 5339, + SpvOpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340, + SpvOpTypeAccelerationStructureKHR = 5341, + SpvOpTypeAccelerationStructureNV = 5341, + SpvOpExecuteCallableNV = 5344, + SpvOpTypeCooperativeMatrixNV = 5358, + SpvOpCooperativeMatrixLoadNV = 5359, + SpvOpCooperativeMatrixStoreNV = 5360, + SpvOpCooperativeMatrixMulAddNV = 5361, + SpvOpCooperativeMatrixLengthNV = 5362, + SpvOpBeginInvocationInterlockEXT = 5364, + SpvOpEndInvocationInterlockEXT = 5365, + SpvOpDemoteToHelperInvocation = 5380, + SpvOpDemoteToHelperInvocationEXT = 5380, + SpvOpIsHelperInvocationEXT = 5381, + SpvOpConvertUToImageNV = 5391, + SpvOpConvertUToSamplerNV = 5392, + SpvOpConvertImageToUNV = 5393, + SpvOpConvertSamplerToUNV = 5394, + SpvOpConvertUToSampledImageNV = 5395, + SpvOpConvertSampledImageToUNV = 5396, + SpvOpSamplerImageAddressingModeNV = 5397, + SpvOpRawAccessChainNV = 5398, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpSubgroupImageMediaBlockReadINTEL = 5580, + SpvOpSubgroupImageMediaBlockWriteINTEL = 5581, + SpvOpUCountLeadingZerosINTEL = 5585, + SpvOpUCountTrailingZerosINTEL = 5586, + SpvOpAbsISubINTEL = 5587, + SpvOpAbsUSubINTEL = 5588, + SpvOpIAddSatINTEL = 5589, + SpvOpUAddSatINTEL = 5590, + SpvOpIAverageINTEL = 5591, + SpvOpUAverageINTEL = 5592, + SpvOpIAverageRoundedINTEL = 5593, + SpvOpUAverageRoundedINTEL = 5594, + SpvOpISubSatINTEL = 5595, + SpvOpUSubSatINTEL = 5596, + SpvOpIMul32x16INTEL = 5597, + SpvOpUMul32x16INTEL = 5598, + SpvOpConstantFunctionPointerINTEL = 5600, + SpvOpFunctionPointerCallINTEL = 5601, + SpvOpAsmTargetINTEL = 5609, + SpvOpAsmINTEL = 5610, + SpvOpAsmCallINTEL = 5611, + SpvOpAtomicFMinEXT = 5614, + SpvOpAtomicFMaxEXT = 5615, + SpvOpAssumeTrueKHR = 5630, + SpvOpExpectKHR = 5631, + SpvOpDecorateString = 5632, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateString = 5633, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpVmeImageINTEL = 5699, + SpvOpTypeVmeImageINTEL = 5700, + SpvOpTypeAvcImePayloadINTEL = 5701, + SpvOpTypeAvcRefPayloadINTEL = 5702, + SpvOpTypeAvcSicPayloadINTEL = 5703, + SpvOpTypeAvcMcePayloadINTEL = 5704, + SpvOpTypeAvcMceResultINTEL = 5705, + SpvOpTypeAvcImeResultINTEL = 5706, + SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + SpvOpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + SpvOpTypeAvcImeDualReferenceStreaminINTEL = 5710, + SpvOpTypeAvcRefResultINTEL = 5711, + SpvOpTypeAvcSicResultINTEL = 5712, + SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + SpvOpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + SpvOpSubgroupAvcMceConvertToImeResultINTEL = 5733, + SpvOpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + SpvOpSubgroupAvcMceConvertToRefResultINTEL = 5735, + SpvOpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + SpvOpSubgroupAvcMceConvertToSicResultINTEL = 5737, + SpvOpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + SpvOpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + SpvOpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + SpvOpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + SpvOpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + SpvOpSubgroupAvcImeInitializeINTEL = 5747, + SpvOpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + SpvOpSubgroupAvcImeSetDualReferenceINTEL = 5749, + SpvOpSubgroupAvcImeRefWindowSizeINTEL = 5750, + SpvOpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + SpvOpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + SpvOpSubgroupAvcImeSetWeightedSadINTEL = 5756, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + SpvOpSubgroupAvcImeConvertToMceResultINTEL = 5765, + SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + SpvOpSubgroupAvcImeGetBorderReachedINTEL = 5776, + SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + SpvOpSubgroupAvcFmeInitializeINTEL = 5781, + SpvOpSubgroupAvcBmeInitializeINTEL = 5782, + SpvOpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + SpvOpSubgroupAvcRefConvertToMceResultINTEL = 5790, + SpvOpSubgroupAvcSicInitializeINTEL = 5791, + SpvOpSubgroupAvcSicConfigureSkcINTEL = 5792, + SpvOpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + SpvOpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + SpvOpSubgroupAvcSicEvaluateIpeINTEL = 5803, + SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + SpvOpSubgroupAvcSicConvertToMceResultINTEL = 5808, + SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + SpvOpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + SpvOpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + SpvOpVariableLengthArrayINTEL = 5818, + SpvOpSaveMemoryINTEL = 5819, + SpvOpRestoreMemoryINTEL = 5820, + SpvOpArbitraryFloatSinCosPiINTEL = 5840, + SpvOpArbitraryFloatCastINTEL = 5841, + SpvOpArbitraryFloatCastFromIntINTEL = 5842, + SpvOpArbitraryFloatCastToIntINTEL = 5843, + SpvOpArbitraryFloatAddINTEL = 5846, + SpvOpArbitraryFloatSubINTEL = 5847, + SpvOpArbitraryFloatMulINTEL = 5848, + SpvOpArbitraryFloatDivINTEL = 5849, + SpvOpArbitraryFloatGTINTEL = 5850, + SpvOpArbitraryFloatGEINTEL = 5851, + SpvOpArbitraryFloatLTINTEL = 5852, + SpvOpArbitraryFloatLEINTEL = 5853, + SpvOpArbitraryFloatEQINTEL = 5854, + SpvOpArbitraryFloatRecipINTEL = 5855, + SpvOpArbitraryFloatRSqrtINTEL = 5856, + SpvOpArbitraryFloatCbrtINTEL = 5857, + SpvOpArbitraryFloatHypotINTEL = 5858, + SpvOpArbitraryFloatSqrtINTEL = 5859, + SpvOpArbitraryFloatLogINTEL = 5860, + SpvOpArbitraryFloatLog2INTEL = 5861, + SpvOpArbitraryFloatLog10INTEL = 5862, + SpvOpArbitraryFloatLog1pINTEL = 5863, + SpvOpArbitraryFloatExpINTEL = 5864, + SpvOpArbitraryFloatExp2INTEL = 5865, + SpvOpArbitraryFloatExp10INTEL = 5866, + SpvOpArbitraryFloatExpm1INTEL = 5867, + SpvOpArbitraryFloatSinINTEL = 5868, + SpvOpArbitraryFloatCosINTEL = 5869, + SpvOpArbitraryFloatSinCosINTEL = 5870, + SpvOpArbitraryFloatSinPiINTEL = 5871, + SpvOpArbitraryFloatCosPiINTEL = 5872, + SpvOpArbitraryFloatASinINTEL = 5873, + SpvOpArbitraryFloatASinPiINTEL = 5874, + SpvOpArbitraryFloatACosINTEL = 5875, + SpvOpArbitraryFloatACosPiINTEL = 5876, + SpvOpArbitraryFloatATanINTEL = 5877, + SpvOpArbitraryFloatATanPiINTEL = 5878, + SpvOpArbitraryFloatATan2INTEL = 5879, + SpvOpArbitraryFloatPowINTEL = 5880, + SpvOpArbitraryFloatPowRINTEL = 5881, + SpvOpArbitraryFloatPowNINTEL = 5882, + SpvOpLoopControlINTEL = 5887, + SpvOpAliasDomainDeclINTEL = 5911, + SpvOpAliasScopeDeclINTEL = 5912, + SpvOpAliasScopeListDeclINTEL = 5913, + SpvOpFixedSqrtINTEL = 5923, + SpvOpFixedRecipINTEL = 5924, + SpvOpFixedRsqrtINTEL = 5925, + SpvOpFixedSinINTEL = 5926, + SpvOpFixedCosINTEL = 5927, + SpvOpFixedSinCosINTEL = 5928, + SpvOpFixedSinPiINTEL = 5929, + SpvOpFixedCosPiINTEL = 5930, + SpvOpFixedSinCosPiINTEL = 5931, + SpvOpFixedLogINTEL = 5932, + SpvOpFixedExpINTEL = 5933, + SpvOpPtrCastToCrossWorkgroupINTEL = 5934, + SpvOpCrossWorkgroupCastToPtrINTEL = 5938, + SpvOpReadPipeBlockingINTEL = 5946, + SpvOpWritePipeBlockingINTEL = 5947, + SpvOpFPGARegINTEL = 5949, + SpvOpRayQueryGetRayTMinKHR = 6016, + SpvOpRayQueryGetRayFlagsKHR = 6017, + SpvOpRayQueryGetIntersectionTKHR = 6018, + SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + SpvOpRayQueryGetIntersectionInstanceIdKHR = 6020, + SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + SpvOpRayQueryGetIntersectionGeometryIndexKHR = 6022, + SpvOpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + SpvOpRayQueryGetIntersectionBarycentricsKHR = 6024, + SpvOpRayQueryGetIntersectionFrontFaceKHR = 6025, + SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + SpvOpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + SpvOpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + SpvOpRayQueryGetWorldRayDirectionKHR = 6029, + SpvOpRayQueryGetWorldRayOriginKHR = 6030, + SpvOpRayQueryGetIntersectionObjectToWorldKHR = 6031, + SpvOpRayQueryGetIntersectionWorldToObjectKHR = 6032, + SpvOpAtomicFAddEXT = 6035, + SpvOpTypeBufferSurfaceINTEL = 6086, + SpvOpTypeStructContinuedINTEL = 6090, + SpvOpConstantCompositeContinuedINTEL = 6091, + SpvOpSpecConstantCompositeContinuedINTEL = 6092, + SpvOpCompositeConstructContinuedINTEL = 6096, + SpvOpConvertFToBF16INTEL = 6116, + SpvOpConvertBF16ToFINTEL = 6117, + SpvOpControlBarrierArriveINTEL = 6142, + SpvOpControlBarrierWaitINTEL = 6143, + SpvOpGroupIMulKHR = 6401, + SpvOpGroupFMulKHR = 6402, + SpvOpGroupBitwiseAndKHR = 6403, + SpvOpGroupBitwiseOrKHR = 6404, + SpvOpGroupBitwiseXorKHR = 6405, + SpvOpGroupLogicalAndKHR = 6406, + SpvOpGroupLogicalOrKHR = 6407, + SpvOpGroupLogicalXorKHR = 6408, + SpvOpMaskedGatherINTEL = 6428, + SpvOpMaskedScatterINTEL = 6429, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#ifdef SPV_ENABLE_UTILITY_CODE +#ifndef __cplusplus +#include +#endif +inline void SpvHasResultAndType(SpvOp opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case SpvOpNop: *hasResult = false; *hasResultType = false; break; + case SpvOpUndef: *hasResult = true; *hasResultType = true; break; + case SpvOpSourceContinued: *hasResult = false; *hasResultType = false; break; + case SpvOpSource: *hasResult = false; *hasResultType = false; break; + case SpvOpSourceExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpName: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberName: *hasResult = false; *hasResultType = false; break; + case SpvOpString: *hasResult = true; *hasResultType = false; break; + case SpvOpLine: *hasResult = false; *hasResultType = false; break; + case SpvOpExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpExtInstImport: *hasResult = true; *hasResultType = false; break; + case SpvOpExtInst: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryModel: *hasResult = false; *hasResultType = false; break; + case SpvOpEntryPoint: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionMode: *hasResult = false; *hasResultType = false; break; + case SpvOpCapability: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeVoid: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeBool: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeInt: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFloat: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeVector: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampler: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeStruct: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePointer: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFunction: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeQueue: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePipe: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case SpvOpConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantSampler: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantNull: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case SpvOpFunction: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case SpvOpFunctionCall: *hasResult = true; *hasResultType = true; break; + case SpvOpVariable: *hasResult = true; *hasResultType = true; break; + case SpvOpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case SpvOpLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpStore: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemory: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case SpvOpAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpArrayLength: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case SpvOpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyObject: *hasResult = true; *hasResultType = true; break; + case SpvOpTranspose: *hasResult = true; *hasResultType = true; break; + case SpvOpSampledImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageRead: *hasResult = true; *hasResultType = true; break; + case SpvOpImageWrite: *hasResult = false; *hasResultType = false; break; + case SpvOpImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToU: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSToF: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToF: *hasResult = true; *hasResultType = true; break; + case SpvOpUConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpSConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpFConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case SpvOpBitcast: *hasResult = true; *hasResultType = true; break; + case SpvOpSNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpFNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpISub: *hasResult = true; *hasResultType = true; break; + case SpvOpFSub: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpUDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpSDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpFDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpUMod: *hasResult = true; *hasResultType = true; break; + case SpvOpSRem: *hasResult = true; *hasResultType = true; break; + case SpvOpSMod: *hasResult = true; *hasResultType = true; break; + case SpvOpFRem: *hasResult = true; *hasResultType = true; break; + case SpvOpFMod: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpOuterProduct: *hasResult = true; *hasResultType = true; break; + case SpvOpDot: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddCarry: *hasResult = true; *hasResultType = true; break; + case SpvOpISubBorrow: *hasResult = true; *hasResultType = true; break; + case SpvOpUMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpSMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpAny: *hasResult = true; *hasResultType = true; break; + case SpvOpAll: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNan: *hasResult = true; *hasResultType = true; break; + case SpvOpIsInf: *hasResult = true; *hasResultType = true; break; + case SpvOpIsFinite: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNormal: *hasResult = true; *hasResultType = true; break; + case SpvOpSignBitSet: *hasResult = true; *hasResultType = true; break; + case SpvOpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case SpvOpOrdered: *hasResult = true; *hasResultType = true; break; + case SpvOpUnordered: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNot: *hasResult = true; *hasResultType = true; break; + case SpvOpSelect: *hasResult = true; *hasResultType = true; break; + case SpvOpIEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpINotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpNot: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitReverse: *hasResult = true; *hasResultType = true; break; + case SpvOpBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdx: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdy: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidth: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyFine: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpEmitVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpControlBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicStore: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicISub: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicOr: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicXor: *hasResult = true; *hasResultType = true; break; + case SpvOpPhi: *hasResult = true; *hasResultType = true; break; + case SpvOpLoopMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpLabel: *hasResult = true; *hasResultType = false; break; + case SpvOpBranch: *hasResult = false; *hasResultType = false; break; + case SpvOpBranchConditional: *hasResult = false; *hasResultType = false; break; + case SpvOpSwitch: *hasResult = false; *hasResultType = false; break; + case SpvOpKill: *hasResult = false; *hasResultType = false; break; + case SpvOpReturn: *hasResult = false; *hasResultType = false; break; + case SpvOpReturnValue: *hasResult = false; *hasResultType = false; break; + case SpvOpUnreachable: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case SpvOpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case SpvOpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case SpvOpRetainEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case SpvOpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case SpvOpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case SpvOpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case SpvOpNoLine: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case SpvOpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case SpvOpSizeOf: *hasResult = true; *hasResultType = true; break; + case SpvOpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case SpvOpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case SpvOpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorateId: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrDiff: *hasResult = true; *hasResultType = true; break; + case SpvOpColorAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpDepthAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpStencilAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformRotateKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpTraceRayKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpExecuteCallableKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpConvertUToAccelerationStructureKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpIgnoreIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpTerminateRayKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpSDot: *hasResult = true; *hasResultType = true; break; + case SpvOpUDot: *hasResult = true; *hasResultType = true; break; + case SpvOpSUDot: *hasResult = true; *hasResultType = true; break; + case SpvOpSDotAccSat: *hasResult = true; *hasResultType = true; break; + case SpvOpUDotAccSat: *hasResult = true; *hasResultType = true; break; + case SpvOpSUDotAccSat: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeCooperativeMatrixKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpCooperativeMatrixLoadKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixStoreKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixMulAddKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixLengthKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeRayQueryKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleWeightedQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBoxFilterQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchSSDQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchSADQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchWindowSSDQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchWindowSADQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchGatherSSDQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchGatherSADQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpFinalizeNodePayloadsAMDX: *hasResult = false; *hasResultType = false; break; + case SpvOpFinishWritingNodePayloadAMDX: *hasResult = true; *hasResultType = true; break; + case SpvOpInitializeNodePayloadsAMDX: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupNonUniformQuadAllKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadAnyKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectRecordHitMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordHitWithIndexMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordMissMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetWorldToObjectNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectToWorldNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectRayOriginNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetShaderRecordBufferHandleNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetShaderBindingTableRecordIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectRecordEmptyNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectTraceRayNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordHitNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordHitWithIndexNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordMissNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectExecuteShaderNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetCurrentTimeNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetAttributesNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetHitKindNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetPrimitiveIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetGeometryIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetInstanceIdNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetInstanceCustomIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetWorldRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetWorldRayOriginNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetRayTMaxNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetRayTMinNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsEmptyNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsHitNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsMissNV: *hasResult = true; *hasResultType = true; break; + case SpvOpReorderThreadWithHitObjectNV: *hasResult = false; *hasResultType = false; break; + case SpvOpReorderThreadWithHintNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeHitObjectNV: *hasResult = true; *hasResultType = false; break; + case SpvOpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case SpvOpEmitMeshTasksEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpSetMeshOutputsEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case SpvOpFetchMicroTriangleVertexPositionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpFetchMicroTriangleVertexBarycentricNV: *hasResult = true; *hasResultType = true; break; + case SpvOpReportIntersectionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGetIntersectionTriangleVertexPositionsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; + case SpvOpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case SpvOpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case SpvOpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpDemoteToHelperInvocation: *hasResult = false; *hasResultType = false; break; + case SpvOpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToImageNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToSamplerNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertImageToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSamplerToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToSampledImageNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSampledImageToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpSamplerImageAddressingModeNV: *hasResult = false; *hasResultType = false; break; + case SpvOpRawAccessChainNV: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAsmTargetINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAsmINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAsmCallINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFMinEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFMaxEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpAssumeTrueKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpExpectKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpVariableLengthArrayINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSaveMemoryINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpRestoreMemoryINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpArbitraryFloatSinCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCastINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCastFromIntINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCastToIntINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatAddINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatMulINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatDivINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatGTINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatGEINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLTINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLEINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatEQINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatRecipINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatRSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCbrtINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatHypotINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLogINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLog2INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLog10INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLog1pINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExpINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExp2INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExp10INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExpm1INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSinINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatASinINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatASinPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatACosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatACosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatATanINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatATanPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatATan2INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatPowINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatPowRINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatPowNINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpAliasDomainDeclINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpAliasScopeDeclINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpAliasScopeListDeclINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpFixedSqrtINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedRecipINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedRsqrtINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedCosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedLogINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedExpINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrCastToCrossWorkgroupINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpCrossWorkgroupCastToPtrINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpReadPipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFPGARegINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeBufferSurfaceINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeStructContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSpecConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpCompositeConstructContinuedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToBF16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertBF16ToFINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpControlBarrierArriveINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpControlBarrierWaitINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupIMulKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMulKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBitwiseAndKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBitwiseOrKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBitwiseXorKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupLogicalAndKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupLogicalOrKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupLogicalXorKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpMaskedGatherINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpMaskedScatterINTEL: *hasResult = false; *hasResultType = false; break; + } +} +#endif /* SPV_ENABLE_UTILITY_CODE */ + +#endif diff --git a/src/spirv_cross_c.h b/src/spirv_cross_c.h new file mode 100644 index 0000000..4736996 --- /dev/null +++ b/src/spirv_cross_c.h @@ -0,0 +1,1101 @@ +/* + * Copyright 2019-2021 Hans-Kristian Arntzen + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_C_API_H +#define SPIRV_CROSS_C_API_H + +#include +#include "spirv.h" + +/* + * C89-compatible wrapper for SPIRV-Cross' API. + * Documentation here is sparse unless the behavior does not map 1:1 with C++ API. + * It is recommended to look at the canonical C++ API for more detailed information. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Bumped if ABI or API breaks backwards compatibility. */ +#define SPVC_C_API_VERSION_MAJOR 0 +/* Bumped if APIs or enumerations are added in a backwards compatible way. */ +#define SPVC_C_API_VERSION_MINOR 59 +/* Bumped if internal implementation details change. */ +#define SPVC_C_API_VERSION_PATCH 0 + +#if !defined(SPVC_PUBLIC_API) +#if defined(SPVC_EXPORT_SYMBOLS) +/* Exports symbols. Standard C calling convention is used. */ +#if defined(__GNUC__) +#define SPVC_PUBLIC_API __attribute__((visibility("default"))) +#elif defined(_MSC_VER) +#define SPVC_PUBLIC_API __declspec(dllexport) +#else +#define SPVC_PUBLIC_API +#endif +#else +#define SPVC_PUBLIC_API +#endif +#endif + +/* + * Gets the SPVC_C_API_VERSION_* used to build this library. + * Can be used to check for ABI mismatch if so-versioning did not catch it. + */ +SPVC_PUBLIC_API void spvc_get_version(unsigned *major, unsigned *minor, unsigned *patch); + +/* Gets a human readable version string to identify which commit a particular binary was created from. */ +SPVC_PUBLIC_API const char *spvc_get_commit_revision_and_timestamp(void); + +/* These types are opaque to the user. */ +typedef struct spvc_context_s *spvc_context; +typedef struct spvc_parsed_ir_s *spvc_parsed_ir; +typedef struct spvc_compiler_s *spvc_compiler; +typedef struct spvc_compiler_options_s *spvc_compiler_options; +typedef struct spvc_resources_s *spvc_resources; +struct spvc_type_s; +typedef const struct spvc_type_s *spvc_type; +typedef struct spvc_constant_s *spvc_constant; +struct spvc_set_s; +typedef const struct spvc_set_s *spvc_set; + +/* + * Shallow typedefs. All SPIR-V IDs are plain 32-bit numbers, but this helps communicate which data is used. + * Maps to a SPIRType. + */ +typedef SpvId spvc_type_id; +/* Maps to a SPIRVariable. */ +typedef SpvId spvc_variable_id; +/* Maps to a SPIRConstant. */ +typedef SpvId spvc_constant_id; + +/* See C++ API. */ +typedef struct spvc_reflected_resource +{ + spvc_variable_id id; + spvc_type_id base_type_id; + spvc_type_id type_id; + const char *name; +} spvc_reflected_resource; + +typedef struct spvc_reflected_builtin_resource +{ + SpvBuiltIn builtin; + spvc_type_id value_type_id; + spvc_reflected_resource resource; +} spvc_reflected_builtin_resource; + +/* See C++ API. */ +typedef struct spvc_entry_point +{ + SpvExecutionModel execution_model; + const char *name; +} spvc_entry_point; + +/* See C++ API. */ +typedef struct spvc_combined_image_sampler +{ + spvc_variable_id combined_id; + spvc_variable_id image_id; + spvc_variable_id sampler_id; +} spvc_combined_image_sampler; + +/* See C++ API. */ +typedef struct spvc_specialization_constant +{ + spvc_constant_id id; + unsigned constant_id; +} spvc_specialization_constant; + +/* See C++ API. */ +typedef struct spvc_buffer_range +{ + unsigned index; + size_t offset; + size_t range; +} spvc_buffer_range; + +/* See C++ API. */ +typedef struct spvc_hlsl_root_constants +{ + unsigned start; + unsigned end; + unsigned binding; + unsigned space; +} spvc_hlsl_root_constants; + +/* See C++ API. */ +typedef struct spvc_hlsl_vertex_attribute_remap +{ + unsigned location; + const char *semantic; +} spvc_hlsl_vertex_attribute_remap; + +/* + * Be compatible with non-C99 compilers, which do not have stdbool. + * Only recent MSVC compilers supports this for example, and ideally SPIRV-Cross should be linkable + * from a wide range of compilers in its C wrapper. + */ +typedef unsigned char spvc_bool; +#define SPVC_TRUE ((spvc_bool)1) +#define SPVC_FALSE ((spvc_bool)0) + +typedef enum spvc_result +{ + /* Success. */ + SPVC_SUCCESS = 0, + + /* The SPIR-V is invalid. Should have been caught by validation ideally. */ + SPVC_ERROR_INVALID_SPIRV = -1, + + /* The SPIR-V might be valid or invalid, but SPIRV-Cross currently cannot correctly translate this to your target language. */ + SPVC_ERROR_UNSUPPORTED_SPIRV = -2, + + /* If for some reason we hit this, new or malloc failed. */ + SPVC_ERROR_OUT_OF_MEMORY = -3, + + /* Invalid API argument. */ + SPVC_ERROR_INVALID_ARGUMENT = -4, + + SPVC_ERROR_INT_MAX = 0x7fffffff +} spvc_result; + +typedef enum spvc_capture_mode +{ + /* The Parsed IR payload will be copied, and the handle can be reused to create other compiler instances. */ + SPVC_CAPTURE_MODE_COPY = 0, + + /* + * The payload will now be owned by the compiler. + * parsed_ir should now be considered a dead blob and must not be used further. + * This is optimal for performance and should be the go-to option. + */ + SPVC_CAPTURE_MODE_TAKE_OWNERSHIP = 1, + + SPVC_CAPTURE_MODE_INT_MAX = 0x7fffffff +} spvc_capture_mode; + +typedef enum spvc_backend +{ + /* This backend can only perform reflection, no compiler options are supported. Maps to spirv_cross::Compiler. */ + SPVC_BACKEND_NONE = 0, + SPVC_BACKEND_GLSL = 1, /* spirv_cross::CompilerGLSL */ + SPVC_BACKEND_HLSL = 2, /* CompilerHLSL */ + SPVC_BACKEND_MSL = 3, /* CompilerMSL */ + SPVC_BACKEND_CPP = 4, /* CompilerCPP */ + SPVC_BACKEND_JSON = 5, /* CompilerReflection w/ JSON backend */ + SPVC_BACKEND_INT_MAX = 0x7fffffff +} spvc_backend; + +/* Maps to C++ API. */ +typedef enum spvc_resource_type +{ + SPVC_RESOURCE_TYPE_UNKNOWN = 0, + SPVC_RESOURCE_TYPE_UNIFORM_BUFFER = 1, + SPVC_RESOURCE_TYPE_STORAGE_BUFFER = 2, + SPVC_RESOURCE_TYPE_STAGE_INPUT = 3, + SPVC_RESOURCE_TYPE_STAGE_OUTPUT = 4, + SPVC_RESOURCE_TYPE_SUBPASS_INPUT = 5, + SPVC_RESOURCE_TYPE_STORAGE_IMAGE = 6, + SPVC_RESOURCE_TYPE_SAMPLED_IMAGE = 7, + SPVC_RESOURCE_TYPE_ATOMIC_COUNTER = 8, + SPVC_RESOURCE_TYPE_PUSH_CONSTANT = 9, + SPVC_RESOURCE_TYPE_SEPARATE_IMAGE = 10, + SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS = 11, + SPVC_RESOURCE_TYPE_ACCELERATION_STRUCTURE = 12, + SPVC_RESOURCE_TYPE_RAY_QUERY = 13, + SPVC_RESOURCE_TYPE_SHADER_RECORD_BUFFER = 14, + SPVC_RESOURCE_TYPE_INT_MAX = 0x7fffffff +} spvc_resource_type; + +typedef enum spvc_builtin_resource_type +{ + SPVC_BUILTIN_RESOURCE_TYPE_UNKNOWN = 0, + SPVC_BUILTIN_RESOURCE_TYPE_STAGE_INPUT = 1, + SPVC_BUILTIN_RESOURCE_TYPE_STAGE_OUTPUT = 2, + SPVC_BUILTIN_RESOURCE_TYPE_INT_MAX = 0x7fffffff +} spvc_builtin_resource_type; + +/* Maps to spirv_cross::SPIRType::BaseType. */ +typedef enum spvc_basetype +{ + SPVC_BASETYPE_UNKNOWN = 0, + SPVC_BASETYPE_VOID = 1, + SPVC_BASETYPE_BOOLEAN = 2, + SPVC_BASETYPE_INT8 = 3, + SPVC_BASETYPE_UINT8 = 4, + SPVC_BASETYPE_INT16 = 5, + SPVC_BASETYPE_UINT16 = 6, + SPVC_BASETYPE_INT32 = 7, + SPVC_BASETYPE_UINT32 = 8, + SPVC_BASETYPE_INT64 = 9, + SPVC_BASETYPE_UINT64 = 10, + SPVC_BASETYPE_ATOMIC_COUNTER = 11, + SPVC_BASETYPE_FP16 = 12, + SPVC_BASETYPE_FP32 = 13, + SPVC_BASETYPE_FP64 = 14, + SPVC_BASETYPE_STRUCT = 15, + SPVC_BASETYPE_IMAGE = 16, + SPVC_BASETYPE_SAMPLED_IMAGE = 17, + SPVC_BASETYPE_SAMPLER = 18, + SPVC_BASETYPE_ACCELERATION_STRUCTURE = 19, + + SPVC_BASETYPE_INT_MAX = 0x7fffffff +} spvc_basetype; + +#define SPVC_COMPILER_OPTION_COMMON_BIT 0x1000000 +#define SPVC_COMPILER_OPTION_GLSL_BIT 0x2000000 +#define SPVC_COMPILER_OPTION_HLSL_BIT 0x4000000 +#define SPVC_COMPILER_OPTION_MSL_BIT 0x8000000 +#define SPVC_COMPILER_OPTION_LANG_BITS 0x0f000000 +#define SPVC_COMPILER_OPTION_ENUM_BITS 0xffffff + +#define SPVC_MAKE_MSL_VERSION(major, minor, patch) ((major) * 10000 + (minor) * 100 + (patch)) + +/* Maps to C++ API. */ +typedef enum spvc_msl_platform +{ + SPVC_MSL_PLATFORM_IOS = 0, + SPVC_MSL_PLATFORM_MACOS = 1, + SPVC_MSL_PLATFORM_MAX_INT = 0x7fffffff +} spvc_msl_platform; + +/* Maps to C++ API. */ +typedef enum spvc_msl_index_type +{ + SPVC_MSL_INDEX_TYPE_NONE = 0, + SPVC_MSL_INDEX_TYPE_UINT16 = 1, + SPVC_MSL_INDEX_TYPE_UINT32 = 2, + SPVC_MSL_INDEX_TYPE_MAX_INT = 0x7fffffff +} spvc_msl_index_type; + +/* Maps to C++ API. */ +typedef enum spvc_msl_shader_variable_format +{ + SPVC_MSL_SHADER_VARIABLE_FORMAT_OTHER = 0, + SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT8 = 1, + SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT16 = 2, + SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY16 = 3, + SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY32 = 4, + + /* Deprecated names. */ + SPVC_MSL_VERTEX_FORMAT_OTHER = SPVC_MSL_SHADER_VARIABLE_FORMAT_OTHER, + SPVC_MSL_VERTEX_FORMAT_UINT8 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT8, + SPVC_MSL_VERTEX_FORMAT_UINT16 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT16, + SPVC_MSL_SHADER_INPUT_FORMAT_OTHER = SPVC_MSL_SHADER_VARIABLE_FORMAT_OTHER, + SPVC_MSL_SHADER_INPUT_FORMAT_UINT8 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT8, + SPVC_MSL_SHADER_INPUT_FORMAT_UINT16 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT16, + SPVC_MSL_SHADER_INPUT_FORMAT_ANY16 = SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY16, + SPVC_MSL_SHADER_INPUT_FORMAT_ANY32 = SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY32, + + + SPVC_MSL_SHADER_INPUT_FORMAT_INT_MAX = 0x7fffffff +} spvc_msl_shader_variable_format, spvc_msl_shader_input_format, spvc_msl_vertex_format; + +/* Maps to C++ API. Deprecated; use spvc_msl_shader_interface_var. */ +typedef struct spvc_msl_vertex_attribute +{ + unsigned location; + + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + unsigned msl_buffer; + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + unsigned msl_offset; + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + unsigned msl_stride; + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + spvc_bool per_instance; + + spvc_msl_vertex_format format; + SpvBuiltIn builtin; +} spvc_msl_vertex_attribute; + +/* + * Initializes the vertex attribute struct. + */ +SPVC_PUBLIC_API void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr); + +/* Maps to C++ API. Deprecated; use spvc_msl_shader_interface_var_2. */ +typedef struct spvc_msl_shader_interface_var +{ + unsigned location; + spvc_msl_vertex_format format; + SpvBuiltIn builtin; + unsigned vecsize; +} spvc_msl_shader_interface_var, spvc_msl_shader_input; + +/* + * Initializes the shader input struct. + * Deprecated. Use spvc_msl_shader_interface_var_init_2(). + */ +SPVC_PUBLIC_API void spvc_msl_shader_interface_var_init(spvc_msl_shader_interface_var *var); +/* + * Deprecated. Use spvc_msl_shader_interface_var_init_2(). + */ +SPVC_PUBLIC_API void spvc_msl_shader_input_init(spvc_msl_shader_input *input); + +/* Maps to C++ API. */ +typedef enum spvc_msl_shader_variable_rate +{ + SPVC_MSL_SHADER_VARIABLE_RATE_PER_VERTEX = 0, + SPVC_MSL_SHADER_VARIABLE_RATE_PER_PRIMITIVE = 1, + SPVC_MSL_SHADER_VARIABLE_RATE_PER_PATCH = 2, + + SPVC_MSL_SHADER_VARIABLE_RATE_INT_MAX = 0x7fffffff, +} spvc_msl_shader_variable_rate; + +/* Maps to C++ API. */ +typedef struct spvc_msl_shader_interface_var_2 +{ + unsigned location; + spvc_msl_shader_variable_format format; + SpvBuiltIn builtin; + unsigned vecsize; + spvc_msl_shader_variable_rate rate; +} spvc_msl_shader_interface_var_2; + +/* + * Initializes the shader interface variable struct. + */ +SPVC_PUBLIC_API void spvc_msl_shader_interface_var_init_2(spvc_msl_shader_interface_var_2 *var); + +/* Maps to C++ API. */ +typedef struct spvc_msl_resource_binding +{ + SpvExecutionModel stage; + unsigned desc_set; + unsigned binding; + unsigned msl_buffer; + unsigned msl_texture; + unsigned msl_sampler; +} spvc_msl_resource_binding; + +/* + * Initializes the resource binding struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding); + +#define SPVC_MSL_PUSH_CONSTANT_DESC_SET (~(0u)) +#define SPVC_MSL_PUSH_CONSTANT_BINDING (0) +#define SPVC_MSL_SWIZZLE_BUFFER_BINDING (~(1u)) +#define SPVC_MSL_BUFFER_SIZE_BUFFER_BINDING (~(2u)) +#define SPVC_MSL_ARGUMENT_BUFFER_BINDING (~(3u)) + +/* Obsolete. Sticks around for backwards compatibility. */ +#define SPVC_MSL_AUX_BUFFER_STRUCT_VERSION 1 + +/* Runtime check for incompatibility. Obsolete. */ +SPVC_PUBLIC_API unsigned spvc_msl_get_aux_buffer_struct_version(void); + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_coord +{ + SPVC_MSL_SAMPLER_COORD_NORMALIZED = 0, + SPVC_MSL_SAMPLER_COORD_PIXEL = 1, + SPVC_MSL_SAMPLER_INT_MAX = 0x7fffffff +} spvc_msl_sampler_coord; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_filter +{ + SPVC_MSL_SAMPLER_FILTER_NEAREST = 0, + SPVC_MSL_SAMPLER_FILTER_LINEAR = 1, + SPVC_MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff +} spvc_msl_sampler_filter; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_mip_filter +{ + SPVC_MSL_SAMPLER_MIP_FILTER_NONE = 0, + SPVC_MSL_SAMPLER_MIP_FILTER_NEAREST = 1, + SPVC_MSL_SAMPLER_MIP_FILTER_LINEAR = 2, + SPVC_MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff +} spvc_msl_sampler_mip_filter; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_address +{ + SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0, + SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1, + SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2, + SPVC_MSL_SAMPLER_ADDRESS_REPEAT = 3, + SPVC_MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4, + SPVC_MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff +} spvc_msl_sampler_address; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_compare_func +{ + SPVC_MSL_SAMPLER_COMPARE_FUNC_NEVER = 0, + SPVC_MSL_SAMPLER_COMPARE_FUNC_LESS = 1, + SPVC_MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2, + SPVC_MSL_SAMPLER_COMPARE_FUNC_GREATER = 3, + SPVC_MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4, + SPVC_MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5, + SPVC_MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6, + SPVC_MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7, + SPVC_MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff +} spvc_msl_sampler_compare_func; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_border_color +{ + SPVC_MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0, + SPVC_MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1, + SPVC_MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2, + SPVC_MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff +} spvc_msl_sampler_border_color; + +/* Maps to C++ API. */ +typedef enum spvc_msl_format_resolution +{ + SPVC_MSL_FORMAT_RESOLUTION_444 = 0, + SPVC_MSL_FORMAT_RESOLUTION_422, + SPVC_MSL_FORMAT_RESOLUTION_420, + SPVC_MSL_FORMAT_RESOLUTION_INT_MAX = 0x7fffffff +} spvc_msl_format_resolution; + +/* Maps to C++ API. */ +typedef enum spvc_msl_chroma_location +{ + SPVC_MSL_CHROMA_LOCATION_COSITED_EVEN = 0, + SPVC_MSL_CHROMA_LOCATION_MIDPOINT, + SPVC_MSL_CHROMA_LOCATION_INT_MAX = 0x7fffffff +} spvc_msl_chroma_location; + +/* Maps to C++ API. */ +typedef enum spvc_msl_component_swizzle +{ + SPVC_MSL_COMPONENT_SWIZZLE_IDENTITY = 0, + SPVC_MSL_COMPONENT_SWIZZLE_ZERO, + SPVC_MSL_COMPONENT_SWIZZLE_ONE, + SPVC_MSL_COMPONENT_SWIZZLE_R, + SPVC_MSL_COMPONENT_SWIZZLE_G, + SPVC_MSL_COMPONENT_SWIZZLE_B, + SPVC_MSL_COMPONENT_SWIZZLE_A, + SPVC_MSL_COMPONENT_SWIZZLE_INT_MAX = 0x7fffffff +} spvc_msl_component_swizzle; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_ycbcr_model_conversion +{ + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_INT_MAX = 0x7fffffff +} spvc_msl_sampler_ycbcr_model_conversion; + +/* Maps to C+ API. */ +typedef enum spvc_msl_sampler_ycbcr_range +{ + SPVC_MSL_SAMPLER_YCBCR_RANGE_ITU_FULL = 0, + SPVC_MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW, + SPVC_MSL_SAMPLER_YCBCR_RANGE_INT_MAX = 0x7fffffff +} spvc_msl_sampler_ycbcr_range; + +/* Maps to C++ API. */ +typedef struct spvc_msl_constexpr_sampler +{ + spvc_msl_sampler_coord coord; + spvc_msl_sampler_filter min_filter; + spvc_msl_sampler_filter mag_filter; + spvc_msl_sampler_mip_filter mip_filter; + spvc_msl_sampler_address s_address; + spvc_msl_sampler_address t_address; + spvc_msl_sampler_address r_address; + spvc_msl_sampler_compare_func compare_func; + spvc_msl_sampler_border_color border_color; + float lod_clamp_min; + float lod_clamp_max; + int max_anisotropy; + + spvc_bool compare_enable; + spvc_bool lod_clamp_enable; + spvc_bool anisotropy_enable; +} spvc_msl_constexpr_sampler; + +/* + * Initializes the constexpr sampler struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler); + +/* Maps to the sampler Y'CbCr conversion-related portions of MSLConstexprSampler. See C++ API for defaults and details. */ +typedef struct spvc_msl_sampler_ycbcr_conversion +{ + unsigned planes; + spvc_msl_format_resolution resolution; + spvc_msl_sampler_filter chroma_filter; + spvc_msl_chroma_location x_chroma_offset; + spvc_msl_chroma_location y_chroma_offset; + spvc_msl_component_swizzle swizzle[4]; + spvc_msl_sampler_ycbcr_model_conversion ycbcr_model; + spvc_msl_sampler_ycbcr_range ycbcr_range; + unsigned bpc; +} spvc_msl_sampler_ycbcr_conversion; + +/* + * Initializes the constexpr sampler struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_msl_sampler_ycbcr_conversion_init(spvc_msl_sampler_ycbcr_conversion *conv); + +/* Maps to C++ API. */ +typedef enum spvc_hlsl_binding_flag_bits +{ + SPVC_HLSL_BINDING_AUTO_NONE_BIT = 0, + SPVC_HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0, + SPVC_HLSL_BINDING_AUTO_CBV_BIT = 1 << 1, + SPVC_HLSL_BINDING_AUTO_SRV_BIT = 1 << 2, + SPVC_HLSL_BINDING_AUTO_UAV_BIT = 1 << 3, + SPVC_HLSL_BINDING_AUTO_SAMPLER_BIT = 1 << 4, + SPVC_HLSL_BINDING_AUTO_ALL = 0x7fffffff +} spvc_hlsl_binding_flag_bits; +typedef unsigned spvc_hlsl_binding_flags; + +#define SPVC_HLSL_PUSH_CONSTANT_DESC_SET (~(0u)) +#define SPVC_HLSL_PUSH_CONSTANT_BINDING (0) + +/* Maps to C++ API. */ +typedef struct spvc_hlsl_resource_binding_mapping +{ + unsigned register_space; + unsigned register_binding; +} spvc_hlsl_resource_binding_mapping; + +typedef struct spvc_hlsl_resource_binding +{ + SpvExecutionModel stage; + unsigned desc_set; + unsigned binding; + + spvc_hlsl_resource_binding_mapping cbv, uav, srv, sampler; +} spvc_hlsl_resource_binding; + +/* + * Initializes the resource binding struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_hlsl_resource_binding_init(spvc_hlsl_resource_binding *binding); + +/* Maps to the various spirv_cross::Compiler*::Option structures. See C++ API for defaults and details. */ +typedef enum spvc_compiler_option +{ + SPVC_COMPILER_OPTION_UNKNOWN = 0, + + SPVC_COMPILER_OPTION_FORCE_TEMPORARY = 1 | SPVC_COMPILER_OPTION_COMMON_BIT, + SPVC_COMPILER_OPTION_FLATTEN_MULTIDIMENSIONAL_ARRAYS = 2 | SPVC_COMPILER_OPTION_COMMON_BIT, + SPVC_COMPILER_OPTION_FIXUP_DEPTH_CONVENTION = 3 | SPVC_COMPILER_OPTION_COMMON_BIT, + SPVC_COMPILER_OPTION_FLIP_VERTEX_Y = 4 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_GLSL_SUPPORT_NONZERO_BASE_INSTANCE = 5 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_SEPARATE_SHADER_OBJECTS = 6 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ENABLE_420PACK_EXTENSION = 7 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_VERSION = 8 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ES = 9 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS = 10 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_FLOAT_PRECISION_HIGHP = 11 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_INT_PRECISION_HIGHP = 12 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL = 13 | SPVC_COMPILER_OPTION_HLSL_BIT, + SPVC_COMPILER_OPTION_HLSL_POINT_SIZE_COMPAT = 14 | SPVC_COMPILER_OPTION_HLSL_BIT, + SPVC_COMPILER_OPTION_HLSL_POINT_COORD_COMPAT = 15 | SPVC_COMPILER_OPTION_HLSL_BIT, + SPVC_COMPILER_OPTION_HLSL_SUPPORT_NONZERO_BASE_VERTEX_BASE_INSTANCE = 16 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_VERSION = 17 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_TEXEL_BUFFER_TEXTURE_WIDTH = 18 | SPVC_COMPILER_OPTION_MSL_BIT, + + /* Obsolete, use SWIZZLE_BUFFER_INDEX instead. */ + SPVC_COMPILER_OPTION_MSL_AUX_BUFFER_INDEX = 19 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SWIZZLE_BUFFER_INDEX = 19 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_MSL_INDIRECT_PARAMS_BUFFER_INDEX = 20 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_OUTPUT_BUFFER_INDEX = 21 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_OUTPUT_BUFFER_INDEX = 22 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_TESS_FACTOR_OUTPUT_BUFFER_INDEX = 23 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_WORKGROUP_INDEX = 24 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_BUILTIN = 25 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DISABLE_RASTERIZATION = 26 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_CAPTURE_OUTPUT_TO_BUFFER = 27 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SWIZZLE_TEXTURE_SAMPLES = 28 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS = 29 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_TESS_DOMAIN_ORIGIN_LOWER_LEFT = 30 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_PLATFORM = 31 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS = 32 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_EMIT_PUSH_CONSTANT_AS_UNIFORM_BUFFER = 33 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_TEXTURE_BUFFER_NATIVE = 34 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_EMIT_UNIFORM_BUFFER_AS_PLAIN_UNIFORMS = 35 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_BUFFER_SIZE_BUFFER_INDEX = 36 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_EMIT_LINE_DIRECTIVES = 37 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_MSL_MULTIVIEW = 38 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VIEW_MASK_BUFFER_INDEX = 39 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DEVICE_INDEX = 40 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VIEW_INDEX_FROM_DEVICE_INDEX = 41 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DISPATCH_BASE = 42 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DYNAMIC_OFFSETS_BUFFER_INDEX = 43 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_TEXTURE_1D_AS_2D = 44 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_BASE_INDEX_ZERO = 45 | SPVC_COMPILER_OPTION_MSL_BIT, + + /* Obsolete. Use MSL_FRAMEBUFFER_FETCH_SUBPASS instead. */ + SPVC_COMPILER_OPTION_MSL_IOS_FRAMEBUFFER_FETCH_SUBPASS = 46 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS = 46 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_MSL_INVARIANT_FP_MATH = 47 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_EMULATE_CUBEMAP_ARRAY = 48 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING = 49 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES = 50 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_NATIVE_ARRAYS = 51 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_ENABLE_STORAGE_IMAGE_QUALIFIER_DEDUCTION = 52 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_HLSL_FORCE_STORAGE_BUFFER_AS_UAV = 53 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_FORCE_ZERO_INITIALIZED_VARIABLES = 54 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_HLSL_NONWRITABLE_UAV_TEXTURE_AS_SRV = 55 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_OUTPUT_MASK = 56 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_DEPTH_BUILTIN = 57 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_STENCIL_REF_BUILTIN = 58 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_CLIP_DISTANCE_USER_VARYING = 59 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_HLSL_ENABLE_16BIT_TYPES = 60 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_MULTI_PATCH_WORKGROUP = 61 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_BUFFER_INDEX = 62 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_INDEX_BUFFER_INDEX = 63 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VERTEX_FOR_TESSELLATION = 64 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VERTEX_INDEX_TYPE = 65 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_FORCE_FLATTENED_IO_BLOCKS = 66 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_MULTIVIEW_LAYERED_RENDERING = 67 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ARRAYED_SUBPASS_INPUT = 68 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_R32UI_LINEAR_TEXTURE_ALIGNMENT = 69 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_R32UI_ALIGNMENT_CONSTANT_ID = 70 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_HLSL_FLATTEN_MATRIX_VERTEX_INPUT_SEMANTICS = 71 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_IOS_USE_SIMDGROUP_FUNCTIONS = 72 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_EMULATE_SUBGROUPS = 73 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FIXED_SUBGROUP_SIZE = 74 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_SAMPLE_RATE_SHADING = 75 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_IOS_SUPPORT_BASE_VERTEX_INSTANCE = 76 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_OVR_MULTIVIEW_VIEW_COUNT = 77 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_RELAX_NAN_CHECKS = 78 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_MSL_RAW_BUFFER_TESE_INPUT = 79 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_INPUT_BUFFER_INDEX = 80 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_MANUAL_HELPER_INVOCATION_UPDATES = 81 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_CHECK_DISCARDED_FRAG_STORES = 82 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_ENABLE_ROW_MAJOR_LOAD_WORKAROUND = 83 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS_TIER = 84 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SAMPLE_DREF_LOD_ARRAY_AS_GRAD = 85 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_READWRITE_TEXTURE_FENCES = 86 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_REPLACE_RECURSIVE_INPUTS = 87 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_AGX_MANUAL_CUBE_GRAD_FIXUP = 88 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff +} spvc_compiler_option; + +/* + * Context is the highest-level API construct. + * The context owns all memory allocations made by its child object hierarchy, including various non-opaque structs and strings. + * This means that the API user only has to care about one "destroy" call ever when using the C API. + * All pointers handed out by the APIs are only valid as long as the context + * is alive and spvc_context_release_allocations has not been called. + */ +SPVC_PUBLIC_API spvc_result spvc_context_create(spvc_context *context); + +/* Frees all memory allocations and objects associated with the context and its child objects. */ +SPVC_PUBLIC_API void spvc_context_destroy(spvc_context context); + +/* Frees all memory allocations and objects associated with the context and its child objects, but keeps the context alive. */ +SPVC_PUBLIC_API void spvc_context_release_allocations(spvc_context context); + +/* Get the string for the last error which was logged. */ +SPVC_PUBLIC_API const char *spvc_context_get_last_error_string(spvc_context context); + +/* Get notified in a callback when an error triggers. Useful for debugging. */ +typedef void (*spvc_error_callback)(void *userdata, const char *error); +SPVC_PUBLIC_API void spvc_context_set_error_callback(spvc_context context, spvc_error_callback cb, void *userdata); + +/* SPIR-V parsing interface. Maps to Parser which then creates a ParsedIR, and that IR is extracted into the handle. */ +SPVC_PUBLIC_API spvc_result spvc_context_parse_spirv(spvc_context context, const SpvId *spirv, size_t word_count, + spvc_parsed_ir *parsed_ir); + +/* + * Create a compiler backend. Capture mode controls if we construct by copy or move semantics. + * It is always recommended to use SPVC_CAPTURE_MODE_TAKE_OWNERSHIP if you only intend to cross-compile the IR once. + */ +SPVC_PUBLIC_API spvc_result spvc_context_create_compiler(spvc_context context, spvc_backend backend, + spvc_parsed_ir parsed_ir, spvc_capture_mode mode, + spvc_compiler *compiler); + +/* Maps directly to C++ API. */ +SPVC_PUBLIC_API unsigned spvc_compiler_get_current_id_bound(spvc_compiler compiler); + +/* Create compiler options, which will initialize defaults. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_create_compiler_options(spvc_compiler compiler, + spvc_compiler_options *options); +/* Override options. Will return error if e.g. MSL options are used for the HLSL backend, etc. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_options_set_bool(spvc_compiler_options options, + spvc_compiler_option option, spvc_bool value); +SPVC_PUBLIC_API spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, + spvc_compiler_option option, unsigned value); +/* Set compiler options. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_install_compiler_options(spvc_compiler compiler, + spvc_compiler_options options); + +/* Compile IR into a string. *source is owned by the context, and caller must not free it themselves. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_compile(spvc_compiler compiler, const char **source); + +/* Maps to C++ API. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_add_header_line(spvc_compiler compiler, const char *line); +SPVC_PUBLIC_API spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *ext); +SPVC_PUBLIC_API size_t spvc_compiler_get_num_required_extensions(spvc_compiler compiler); +SPVC_PUBLIC_API const char *spvc_compiler_get_required_extension(spvc_compiler compiler, size_t index); +SPVC_PUBLIC_API spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id); + +SPVC_PUBLIC_API spvc_result spvc_compiler_mask_stage_output_by_location(spvc_compiler compiler, + unsigned location, unsigned component); +SPVC_PUBLIC_API spvc_result spvc_compiler_mask_stage_output_by_builtin(spvc_compiler compiler, SpvBuiltIn builtin); + +/* + * HLSL specifics. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler, + const spvc_hlsl_root_constants *constant_info, + size_t count); +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_add_vertex_attribute_remap(spvc_compiler compiler, + const spvc_hlsl_vertex_attribute_remap *remap, + size_t remaps); +SPVC_PUBLIC_API spvc_variable_id spvc_compiler_hlsl_remap_num_workgroups_builtin(spvc_compiler compiler); + +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler, + spvc_hlsl_binding_flags flags); + +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_add_resource_binding(spvc_compiler compiler, + const spvc_hlsl_resource_binding *binding); +SPVC_PUBLIC_API spvc_bool spvc_compiler_hlsl_is_resource_used(spvc_compiler compiler, + SpvExecutionModel model, + unsigned set, + unsigned binding); + +/* + * MSL specifics. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler); + +/* Obsolete. Renamed to needs_swizzle_buffer. */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_aux_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_swizzle_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_buffer_size_buffer(spvc_compiler compiler); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_output_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_patch_output_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_input_threadgroup_mem(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler, + const spvc_msl_vertex_attribute *attrs); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler, + const spvc_msl_resource_binding *binding); +/* Deprecated; use spvc_compiler_msl_add_shader_input_2(). */ +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler, + const spvc_msl_shader_interface_var *input); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_input_2(spvc_compiler compiler, + const spvc_msl_shader_interface_var_2 *input); +/* Deprecated; use spvc_compiler_msl_add_shader_output_2(). */ +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_output(spvc_compiler compiler, + const spvc_msl_shader_interface_var *output); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_output_2(spvc_compiler compiler, + const spvc_msl_shader_interface_var_2 *output); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_argument_buffer_device_address_space(spvc_compiler compiler, unsigned desc_set, spvc_bool device_address); + +/* Obsolete, use is_shader_input_used. */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_shader_input_used(spvc_compiler compiler, unsigned location); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_shader_output_used(spvc_compiler compiler, unsigned location); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_resource_used(spvc_compiler compiler, + SpvExecutionModel model, + unsigned set, + unsigned binding); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id, const spvc_msl_constexpr_sampler *sampler); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding(spvc_compiler compiler, unsigned desc_set, unsigned binding, const spvc_msl_constexpr_sampler *sampler); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_ycbcr(spvc_compiler compiler, spvc_variable_id id, const spvc_msl_constexpr_sampler *sampler, const spvc_msl_sampler_ycbcr_conversion *conv); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding_ycbcr(spvc_compiler compiler, unsigned desc_set, unsigned binding, const spvc_msl_constexpr_sampler *sampler, const spvc_msl_sampler_ycbcr_conversion *conv); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location, unsigned components); + +SPVC_PUBLIC_API unsigned spvc_compiler_msl_get_automatic_resource_binding(spvc_compiler compiler, spvc_variable_id id); +SPVC_PUBLIC_API unsigned spvc_compiler_msl_get_automatic_resource_binding_secondary(spvc_compiler compiler, spvc_variable_id id); + +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_dynamic_buffer(spvc_compiler compiler, unsigned desc_set, unsigned binding, unsigned index); + +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_inline_uniform_block(spvc_compiler compiler, unsigned desc_set, unsigned binding); + +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_combined_sampler_suffix(spvc_compiler compiler, const char *suffix); +SPVC_PUBLIC_API const char *spvc_compiler_msl_get_combined_sampler_suffix(spvc_compiler compiler); + +/* + * Reflect resources. + * Maps almost 1:1 to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_active_interface_variables(spvc_compiler compiler, spvc_set *set); +SPVC_PUBLIC_API spvc_result spvc_compiler_set_enabled_interface_variables(spvc_compiler compiler, spvc_set set); +SPVC_PUBLIC_API spvc_result spvc_compiler_create_shader_resources(spvc_compiler compiler, spvc_resources *resources); +SPVC_PUBLIC_API spvc_result spvc_compiler_create_shader_resources_for_active_variables(spvc_compiler compiler, + spvc_resources *resources, + spvc_set active); +SPVC_PUBLIC_API spvc_result spvc_resources_get_resource_list_for_type(spvc_resources resources, spvc_resource_type type, + const spvc_reflected_resource **resource_list, + size_t *resource_size); + +SPVC_PUBLIC_API spvc_result spvc_resources_get_builtin_resource_list_for_type( + spvc_resources resources, spvc_builtin_resource_type type, + const spvc_reflected_builtin_resource **resource_list, + size_t *resource_size); + +/* + * Decorations. + * Maps to C++ API. + */ +SPVC_PUBLIC_API void spvc_compiler_set_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration, + unsigned argument); +SPVC_PUBLIC_API void spvc_compiler_set_decoration_string(spvc_compiler compiler, SpvId id, SpvDecoration decoration, + const char *argument); +SPVC_PUBLIC_API void spvc_compiler_set_name(spvc_compiler compiler, SpvId id, const char *argument); +SPVC_PUBLIC_API void spvc_compiler_set_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration, unsigned argument); +SPVC_PUBLIC_API void spvc_compiler_set_member_decoration_string(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration, + const char *argument); +SPVC_PUBLIC_API void spvc_compiler_set_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + const char *argument); +SPVC_PUBLIC_API void spvc_compiler_unset_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration); +SPVC_PUBLIC_API void spvc_compiler_unset_member_decoration(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_has_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration); +SPVC_PUBLIC_API spvc_bool spvc_compiler_has_member_decoration(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_name(spvc_compiler compiler, SpvId id); +SPVC_PUBLIC_API unsigned spvc_compiler_get_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_decoration_string(spvc_compiler compiler, SpvId id, + SpvDecoration decoration); +SPVC_PUBLIC_API unsigned spvc_compiler_get_member_decoration(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_member_decoration_string(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index); + +/* + * Entry points. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_entry_points(spvc_compiler compiler, + const spvc_entry_point **entry_points, + size_t *num_entry_points); +SPVC_PUBLIC_API spvc_result spvc_compiler_set_entry_point(spvc_compiler compiler, const char *name, + SpvExecutionModel model); +SPVC_PUBLIC_API spvc_result spvc_compiler_rename_entry_point(spvc_compiler compiler, const char *old_name, + const char *new_name, SpvExecutionModel model); +SPVC_PUBLIC_API const char *spvc_compiler_get_cleansed_entry_point_name(spvc_compiler compiler, const char *name, + SpvExecutionModel model); +SPVC_PUBLIC_API void spvc_compiler_set_execution_mode(spvc_compiler compiler, SpvExecutionMode mode); +SPVC_PUBLIC_API void spvc_compiler_unset_execution_mode(spvc_compiler compiler, SpvExecutionMode mode); +SPVC_PUBLIC_API void spvc_compiler_set_execution_mode_with_arguments(spvc_compiler compiler, SpvExecutionMode mode, + unsigned arg0, unsigned arg1, unsigned arg2); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_execution_modes(spvc_compiler compiler, const SpvExecutionMode **modes, + size_t *num_modes); +SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument(spvc_compiler compiler, SpvExecutionMode mode); +SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument_by_index(spvc_compiler compiler, + SpvExecutionMode mode, unsigned index); +SPVC_PUBLIC_API SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler); +SPVC_PUBLIC_API void spvc_compiler_update_active_builtins(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_has_active_builtin(spvc_compiler compiler, SpvBuiltIn builtin, SpvStorageClass storage); + +/* + * Type query interface. + * Maps to C++ API, except it's read-only. + */ +SPVC_PUBLIC_API spvc_type spvc_compiler_get_type_handle(spvc_compiler compiler, spvc_type_id id); + +/* Pulls out SPIRType::self. This effectively gives the type ID without array or pointer qualifiers. + * This is necessary when reflecting decoration/name information on members of a struct, + * which are placed in the base type, not the qualified type. + * This is similar to spvc_reflected_resource::base_type_id. */ +SPVC_PUBLIC_API spvc_type_id spvc_type_get_base_type_id(spvc_type type); + +SPVC_PUBLIC_API spvc_basetype spvc_type_get_basetype(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_bit_width(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_vector_size(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_columns(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_num_array_dimensions(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_array_dimension_is_literal(spvc_type type, unsigned dimension); +SPVC_PUBLIC_API SpvId spvc_type_get_array_dimension(spvc_type type, unsigned dimension); +SPVC_PUBLIC_API unsigned spvc_type_get_num_member_types(spvc_type type); +SPVC_PUBLIC_API spvc_type_id spvc_type_get_member_type(spvc_type type, unsigned index); +SPVC_PUBLIC_API SpvStorageClass spvc_type_get_storage_class(spvc_type type); + +/* Image type query. */ +SPVC_PUBLIC_API spvc_type_id spvc_type_get_image_sampled_type(spvc_type type); +SPVC_PUBLIC_API SpvDim spvc_type_get_image_dimension(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_is_depth(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_arrayed(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_multisampled(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_is_storage(spvc_type type); +SPVC_PUBLIC_API SpvImageFormat spvc_type_get_image_storage_format(spvc_type type); +SPVC_PUBLIC_API SpvAccessQualifier spvc_type_get_image_access_qualifier(spvc_type type); + +/* + * Buffer layout query. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_size(spvc_compiler compiler, spvc_type struct_type, size_t *size); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_size_runtime_array(spvc_compiler compiler, + spvc_type struct_type, size_t array_size, size_t *size); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_member_size(spvc_compiler compiler, spvc_type type, unsigned index, size_t *size); + +SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_offset(spvc_compiler compiler, + spvc_type type, unsigned index, unsigned *offset); +SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_array_stride(spvc_compiler compiler, + spvc_type type, unsigned index, unsigned *stride); +SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_matrix_stride(spvc_compiler compiler, + spvc_type type, unsigned index, unsigned *stride); + +/* + * Workaround helper functions. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_build_dummy_sampler_for_combined_images(spvc_compiler compiler, spvc_variable_id *id); +SPVC_PUBLIC_API spvc_result spvc_compiler_build_combined_image_samplers(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_combined_image_samplers(spvc_compiler compiler, + const spvc_combined_image_sampler **samplers, + size_t *num_samplers); + +/* + * Constants + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_specialization_constants(spvc_compiler compiler, + const spvc_specialization_constant **constants, + size_t *num_constants); +SPVC_PUBLIC_API spvc_constant spvc_compiler_get_constant_handle(spvc_compiler compiler, + spvc_constant_id id); + +SPVC_PUBLIC_API spvc_constant_id spvc_compiler_get_work_group_size_specialization_constants(spvc_compiler compiler, + spvc_specialization_constant *x, + spvc_specialization_constant *y, + spvc_specialization_constant *z); + +/* + * Buffer ranges + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_active_buffer_ranges(spvc_compiler compiler, + spvc_variable_id id, + const spvc_buffer_range **ranges, + size_t *num_ranges); + +/* + * No stdint.h until C99, sigh :( + * For smaller types, the result is sign or zero-extended as appropriate. + * Maps to C++ API. + * TODO: The SPIRConstant query interface and modification interface is not quite complete. + */ +SPVC_PUBLIC_API float spvc_constant_get_scalar_fp16(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API float spvc_constant_get_scalar_fp32(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API double spvc_constant_get_scalar_fp64(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u32(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API int spvc_constant_get_scalar_i32(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u16(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API int spvc_constant_get_scalar_i16(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u8(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API int spvc_constant_get_scalar_i8(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API void spvc_constant_get_subconstants(spvc_constant constant, const spvc_constant_id **constituents, size_t *count); +SPVC_PUBLIC_API unsigned long long spvc_constant_get_scalar_u64(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API long long spvc_constant_get_scalar_i64(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API spvc_type_id spvc_constant_get_type(spvc_constant constant); + +/* + * C implementation of the C++ api. + */ +SPVC_PUBLIC_API void spvc_constant_set_scalar_fp16(spvc_constant constant, unsigned column, unsigned row, unsigned short value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_fp32(spvc_constant constant, unsigned column, unsigned row, float value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_fp64(spvc_constant constant, unsigned column, unsigned row, double value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_u32(spvc_constant constant, unsigned column, unsigned row, unsigned value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_i32(spvc_constant constant, unsigned column, unsigned row, int value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_u64(spvc_constant constant, unsigned column, unsigned row, unsigned long long value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_i64(spvc_constant constant, unsigned column, unsigned row, long long value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_u16(spvc_constant constant, unsigned column, unsigned row, unsigned short value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_i16(spvc_constant constant, unsigned column, unsigned row, signed short value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_u8(spvc_constant constant, unsigned column, unsigned row, unsigned char value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_i8(spvc_constant constant, unsigned column, unsigned row, signed char value); + +/* + * Misc reflection + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_get_binary_offset_for_decoration(spvc_compiler compiler, + spvc_variable_id id, + SpvDecoration decoration, + unsigned *word_offset); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_buffer_is_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id); +SPVC_PUBLIC_API spvc_bool spvc_compiler_buffer_get_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id, + spvc_variable_id *counter_id); + +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_capabilities(spvc_compiler compiler, + const SpvCapability **capabilities, + size_t *num_capabilities); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_extensions(spvc_compiler compiler, const char ***extensions, + size_t *num_extensions); + +SPVC_PUBLIC_API const char *spvc_compiler_get_remapped_declared_block_name(spvc_compiler compiler, spvc_variable_id id); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_buffer_block_decorations(spvc_compiler compiler, spvc_variable_id id, + const SpvDecoration **decorations, + size_t *num_decorations); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/vulkan/Refresh_vulkan.c b/src/vulkan/Refresh_vulkan.c new file mode 100644 index 0000000..8672f1a --- /dev/null +++ b/src/vulkan/Refresh_vulkan.c @@ -0,0 +1,13078 @@ +/* Refresh - a cross-platform hardware-accelerated graphics library with modern capabilities + * + * Copyright (c) 2020-2024 Evan Hemsley + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * Evan "cosmonaut" Hemsley + * + */ + +#if REFRESH_VULKAN + +/* Needed for VK_KHR_portability_subset */ +#define VK_ENABLE_BETA_EXTENSIONS + +#define VK_NO_PROTOTYPES +#include + +#include +#include +#include + +#include "Refresh_driver.h" + +#define VULKAN_INTERNAL_clamp(val, min, max) SDL_max(min, SDL_min(val, max)) + +/* Global Vulkan Loader Entry Points */ + +static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; + +#define VULKAN_GLOBAL_FUNCTION(name) \ + static PFN_##name name = NULL; +#include "Refresh_vulkan_vkfuncs.h" + +/* vkInstance/vkDevice function typedefs */ + +#define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \ + typedef ret (VKAPI_CALL *vkfntype_##func) params; +#define VULKAN_DEVICE_FUNCTION(ext, ret, func, params) \ + typedef ret (VKAPI_CALL *vkfntype_##func) params; +#include "Refresh_vulkan_vkfuncs.h" + +typedef struct VulkanExtensions +{ + /* Globally supported */ + Uint8 KHR_swapchain; + /* Core since 1.1 */ + Uint8 KHR_maintenance1; + Uint8 KHR_get_memory_requirements2; + + /* Core since 1.2 */ + Uint8 KHR_driver_properties; + /* EXT, probably not going to be Core */ + Uint8 EXT_vertex_attribute_divisor; + /* Only required for special implementations (i.e. MoltenVK) */ + Uint8 KHR_portability_subset; +} VulkanExtensions; + +/* Defines */ + +#define SMALL_ALLOCATION_THRESHOLD 2097152 /* 2 MiB */ +#define SMALL_ALLOCATION_SIZE 16777216 /* 16 MiB */ +#define LARGE_ALLOCATION_INCREMENT 67108864 /* 64 MiB */ +#define MAX_UBO_SECTION_SIZE 4096 /* 4 KiB */ +#define UNIFORM_BUFFER_SIZE 1048576 /* 1 MiB */ +#define DESCRIPTOR_POOL_STARTING_SIZE 128 +#define MAX_QUERIES 16 +#define WINDOW_PROPERTY_DATA "Refresh_VulkanWindowPropertyData" + +#define IDENTITY_SWIZZLE \ +{ \ + VK_COMPONENT_SWIZZLE_IDENTITY, \ + VK_COMPONENT_SWIZZLE_IDENTITY, \ + VK_COMPONENT_SWIZZLE_IDENTITY, \ + VK_COMPONENT_SWIZZLE_IDENTITY \ +} + +#define NULL_DESC_LAYOUT (VkDescriptorSetLayout) 0 +#define NULL_PIPELINE_LAYOUT (VkPipelineLayout) 0 +#define NULL_RENDER_PASS (Refresh_RenderPass*) 0 + +#define EXPAND_ELEMENTS_IF_NEEDED(arr, initialValue, type) \ + if (arr->count == arr->capacity) \ + { \ + if (arr->capacity == 0) \ + { \ + arr->capacity = initialValue; \ + } \ + else \ + { \ + arr->capacity *= 2; \ + } \ + arr->elements = (type*) SDL_realloc( \ + arr->elements, \ + arr->capacity * sizeof(type) \ + ); \ + } + +#define EXPAND_ARRAY_IF_NEEDED(arr, elementType, newCount, capacity, newCapacity) \ + if (newCount >= capacity) \ + { \ + capacity = newCapacity; \ + arr = (elementType*) SDL_realloc( \ + arr, \ + sizeof(elementType) * capacity \ + ); \ + } + +#define MOVE_ARRAY_CONTENTS_AND_RESET(i, dstArr, dstCount, srcArr, srcCount) \ + for (i = 0; i < srcCount; i += 1) \ + { \ + dstArr[i] = srcArr[i]; \ + } \ + dstCount = srcCount; \ + srcCount = 0; + +/* Conversions */ + +static const Uint8 DEVICE_PRIORITY[] = +{ + 0, /* VK_PHYSICAL_DEVICE_TYPE_OTHER */ + 3, /* VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU */ + 4, /* VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU */ + 2, /* VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU */ + 1 /* VK_PHYSICAL_DEVICE_TYPE_CPU */ +}; + +static VkPresentModeKHR SDLToVK_PresentMode[] = +{ + VK_PRESENT_MODE_FIFO_KHR, + VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_MAILBOX_KHR +}; + +static VkFormat SDLToVK_SurfaceFormat[] = +{ + VK_FORMAT_R8G8B8A8_UNORM, /* R8G8B8A8 */ + VK_FORMAT_B8G8R8A8_UNORM, /* B8G8R8A8 */ + VK_FORMAT_R5G6B5_UNORM_PACK16, /* R5G6B5 */ + VK_FORMAT_A1R5G5B5_UNORM_PACK16, /* A1R5G5B5 */ + VK_FORMAT_B4G4R4A4_UNORM_PACK16, /* B4G4R4A4 */ + VK_FORMAT_A2R10G10B10_UNORM_PACK32, /* A2R10G10B10 */ + VK_FORMAT_A2B10G10R10_UNORM_PACK32, /* A2R10G10B10 */ + VK_FORMAT_R16G16_UNORM, /* R16G16 */ + VK_FORMAT_R16G16B16A16_UNORM, /* R16G16B16A16 */ + VK_FORMAT_R8_UNORM, /* R8 */ + VK_FORMAT_R8_UNORM, /* A8 */ + VK_FORMAT_BC1_RGBA_UNORM_BLOCK, /* BC1 */ + VK_FORMAT_BC2_UNORM_BLOCK, /* BC2 */ + VK_FORMAT_BC3_UNORM_BLOCK, /* BC3 */ + VK_FORMAT_BC7_UNORM_BLOCK, /* BC7 */ + VK_FORMAT_R8G8_SNORM, /* R8G8_SNORM */ + VK_FORMAT_R8G8B8A8_SNORM, /* R8G8B8A8_SNORM */ + VK_FORMAT_R16_SFLOAT, /* R16_SFLOAT */ + VK_FORMAT_R16G16_SFLOAT, /* R16G16_SFLOAT */ + VK_FORMAT_R16G16B16A16_SFLOAT, /* R16G16B16A16_SFLOAT */ + VK_FORMAT_R32_SFLOAT, /* R32_SFLOAT */ + VK_FORMAT_R32G32_SFLOAT, /* R32G32_SFLOAT */ + VK_FORMAT_R32G32B32A32_SFLOAT, /* R32G32B32A32_SFLOAT */ + VK_FORMAT_R8_UINT, /* R8_UINT */ + VK_FORMAT_R8G8_UINT, /* R8G8_UINT */ + VK_FORMAT_R8G8B8A8_UINT, /* R8G8B8A8_UINT */ + VK_FORMAT_R16_UINT, /* R16_UINT */ + VK_FORMAT_R16G16_UINT, /* R16G16_UINT */ + VK_FORMAT_R16G16B16A16_UINT, /* R16G16B16A16_UINT */ + VK_FORMAT_R8G8B8A8_SRGB, /* R8G8B8A8_SRGB */ + VK_FORMAT_B8G8R8A8_SRGB, /* B8G8R8A8_SRGB */ + VK_FORMAT_BC3_SRGB_BLOCK, /* BC3_SRGB */ + VK_FORMAT_BC7_SRGB_BLOCK, /* BC7_SRGB */ + VK_FORMAT_D16_UNORM, /* D16_UNORM */ + VK_FORMAT_X8_D24_UNORM_PACK32, /* D24_UNORM */ + VK_FORMAT_D32_SFLOAT, /* D32_SFLOAT */ + VK_FORMAT_D24_UNORM_S8_UINT, /* D24_UNORM_S8_UINT */ + VK_FORMAT_D32_SFLOAT_S8_UINT, /* D32_SFLOAT_S8_UINT */ +}; + +/* from SWAPCHAINCOMPOSITION */ +static VkFormat SwapchainCompositionToFormat[] = +{ + VK_FORMAT_B8G8R8A8_UNORM, /* SDR */ + VK_FORMAT_B8G8R8A8_SRGB, /* SDR_SRGB */ + VK_FORMAT_R16G16B16A16_SFLOAT, /* HDR */ + VK_FORMAT_A2B10G10R10_UNORM_PACK32 /* HDR_ADVANCED*/ +}; + +/* from SWAPCHAINCOMPOSITION */ +static VkColorSpaceKHR SwapchainCompositionToColorSpace[] = +{ + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT, + VK_COLOR_SPACE_HDR10_ST2084_EXT +}; + +static VkFormat SDLToVK_VertexFormat[] = +{ + VK_FORMAT_R32_UINT, /* UINT */ + VK_FORMAT_R32_SFLOAT, /* FLOAT */ + VK_FORMAT_R32G32_SFLOAT, /* VECTOR2 */ + VK_FORMAT_R32G32B32_SFLOAT, /* VECTOR3 */ + VK_FORMAT_R32G32B32A32_SFLOAT, /* VECTOR4 */ + VK_FORMAT_R8G8B8A8_UNORM, /* COLOR */ + VK_FORMAT_R8G8B8A8_USCALED, /* BYTE4 */ + VK_FORMAT_R16G16_SSCALED, /* SHORT2 */ + VK_FORMAT_R16G16B16A16_SSCALED, /* SHORT4 */ + VK_FORMAT_R16G16_SNORM, /* NORMALIZEDSHORT2 */ + VK_FORMAT_R16G16B16A16_SNORM, /* NORMALIZEDSHORT4 */ + VK_FORMAT_R16G16_SFLOAT, /* HALFVECTOR2 */ + VK_FORMAT_R16G16B16A16_SFLOAT /* HALFVECTOR4 */ +}; + +static VkIndexType SDLToVK_IndexType[] = +{ + VK_INDEX_TYPE_UINT16, + VK_INDEX_TYPE_UINT32 +}; + +static VkPrimitiveTopology SDLToVK_PrimitiveType[] = +{ + VK_PRIMITIVE_TOPOLOGY_POINT_LIST, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP +}; + +static VkPolygonMode SDLToVK_PolygonMode[] = +{ + VK_POLYGON_MODE_FILL, + VK_POLYGON_MODE_LINE, + VK_POLYGON_MODE_POINT +}; + +static VkCullModeFlags SDLToVK_CullMode[] = +{ + VK_CULL_MODE_NONE, + VK_CULL_MODE_FRONT_BIT, + VK_CULL_MODE_BACK_BIT, + VK_CULL_MODE_FRONT_AND_BACK +}; + +static VkFrontFace SDLToVK_FrontFace[] = +{ + VK_FRONT_FACE_COUNTER_CLOCKWISE, + VK_FRONT_FACE_CLOCKWISE +}; + +static VkBlendFactor SDLToVK_BlendFactor[] = +{ + VK_BLEND_FACTOR_ZERO, + VK_BLEND_FACTOR_ONE, + VK_BLEND_FACTOR_SRC_COLOR, + VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR, + VK_BLEND_FACTOR_DST_COLOR, + VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR, + VK_BLEND_FACTOR_SRC_ALPHA, + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + VK_BLEND_FACTOR_DST_ALPHA, + VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA, + VK_BLEND_FACTOR_CONSTANT_COLOR, + VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR, + VK_BLEND_FACTOR_CONSTANT_ALPHA, + VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA, + VK_BLEND_FACTOR_SRC_ALPHA_SATURATE +}; + +static VkBlendOp SDLToVK_BlendOp[] = +{ + VK_BLEND_OP_ADD, + VK_BLEND_OP_SUBTRACT, + VK_BLEND_OP_REVERSE_SUBTRACT, + VK_BLEND_OP_MIN, + VK_BLEND_OP_MAX +}; + +static VkCompareOp SDLToVK_CompareOp[] = +{ + VK_COMPARE_OP_NEVER, + VK_COMPARE_OP_LESS, + VK_COMPARE_OP_EQUAL, + VK_COMPARE_OP_LESS_OR_EQUAL, + VK_COMPARE_OP_GREATER, + VK_COMPARE_OP_NOT_EQUAL, + VK_COMPARE_OP_GREATER_OR_EQUAL, + VK_COMPARE_OP_ALWAYS +}; + +static VkStencilOp SDLToVK_StencilOp[] = +{ + VK_STENCIL_OP_KEEP, + VK_STENCIL_OP_ZERO, + VK_STENCIL_OP_REPLACE, + VK_STENCIL_OP_INCREMENT_AND_CLAMP, + VK_STENCIL_OP_DECREMENT_AND_CLAMP, + VK_STENCIL_OP_INVERT, + VK_STENCIL_OP_INCREMENT_AND_WRAP, + VK_STENCIL_OP_DECREMENT_AND_WRAP +}; + +static VkAttachmentLoadOp SDLToVK_LoadOp[] = +{ + VK_ATTACHMENT_LOAD_OP_LOAD, + VK_ATTACHMENT_LOAD_OP_CLEAR, + VK_ATTACHMENT_LOAD_OP_DONT_CARE +}; + +static VkAttachmentStoreOp SDLToVK_StoreOp[] = +{ + VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_STORE_OP_DONT_CARE +}; + +static VkSampleCountFlagBits SDLToVK_SampleCount[] = +{ + VK_SAMPLE_COUNT_1_BIT, + VK_SAMPLE_COUNT_2_BIT, + VK_SAMPLE_COUNT_4_BIT, + VK_SAMPLE_COUNT_8_BIT, + VK_SAMPLE_COUNT_16_BIT, + VK_SAMPLE_COUNT_32_BIT, + VK_SAMPLE_COUNT_64_BIT +}; + +static VkVertexInputRate SDLToVK_VertexInputRate[] = +{ + VK_VERTEX_INPUT_RATE_VERTEX, + VK_VERTEX_INPUT_RATE_INSTANCE +}; + +static VkFilter SDLToVK_Filter[] = +{ + VK_FILTER_NEAREST, + VK_FILTER_LINEAR +}; + +static VkSamplerMipmapMode SDLToVK_SamplerMipmapMode[] = +{ + VK_SAMPLER_MIPMAP_MODE_NEAREST, + VK_SAMPLER_MIPMAP_MODE_LINEAR +}; + +static VkSamplerAddressMode SDLToVK_SamplerAddressMode[] = +{ + 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 +}; + +static VkBorderColor SDLToVK_BorderColor[] = +{ + VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, + VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + VK_BORDER_COLOR_INT_OPAQUE_BLACK, + VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, + VK_BORDER_COLOR_INT_OPAQUE_WHITE +}; + +/* Structures */ + +typedef struct VulkanMemoryAllocation VulkanMemoryAllocation; +typedef struct VulkanBuffer VulkanBuffer; +typedef struct VulkanBufferContainer VulkanBufferContainer; +typedef struct VulkanTexture VulkanTexture; +typedef struct VulkanTextureContainer VulkanTextureContainer; + +typedef struct VulkanFenceHandle +{ + VkFence fence; + SDL_atomic_t referenceCount; +} VulkanFenceHandle; + +/* Memory Allocation */ + +typedef struct VulkanMemoryFreeRegion +{ + VulkanMemoryAllocation *allocation; + VkDeviceSize offset; + VkDeviceSize size; + Uint32 allocationIndex; + Uint32 sortedIndex; +} VulkanMemoryFreeRegion; + +typedef struct VulkanMemoryUsedRegion +{ + VulkanMemoryAllocation *allocation; + VkDeviceSize offset; + VkDeviceSize size; + VkDeviceSize resourceOffset; /* differs from offset based on alignment*/ + VkDeviceSize resourceSize; /* differs from size based on alignment */ + VkDeviceSize alignment; + Uint8 isBuffer; + union + { + VulkanBuffer *vulkanBuffer; + VulkanTexture *vulkanTexture; + }; +} VulkanMemoryUsedRegion; + +typedef struct VulkanMemorySubAllocator +{ + Uint32 memoryTypeIndex; + VulkanMemoryAllocation **allocations; + Uint32 allocationCount; + VulkanMemoryFreeRegion **sortedFreeRegions; + Uint32 sortedFreeRegionCount; + Uint32 sortedFreeRegionCapacity; +} VulkanMemorySubAllocator; + +struct VulkanMemoryAllocation +{ + VulkanMemorySubAllocator *allocator; + VkDeviceMemory memory; + VkDeviceSize size; + VulkanMemoryUsedRegion **usedRegions; + Uint32 usedRegionCount; + Uint32 usedRegionCapacity; + VulkanMemoryFreeRegion **freeRegions; + Uint32 freeRegionCount; + Uint32 freeRegionCapacity; + Uint8 availableForAllocation; + VkDeviceSize freeSpace; + VkDeviceSize usedSpace; + Uint8 *mapPointer; + SDL_mutex *memoryLock; +}; + +typedef struct VulkanMemoryAllocator +{ + VulkanMemorySubAllocator subAllocators[VK_MAX_MEMORY_TYPES]; +} VulkanMemoryAllocator; + +/* Memory structures */ + +/* We use pointer indirection so that defrag can occur without objects + * needing to be aware of the backing buffers changing. + */ +typedef struct VulkanBufferHandle +{ + VulkanBuffer *vulkanBuffer; + VulkanBufferContainer *container; +} VulkanBufferHandle; + +typedef enum VulkanBufferType +{ + VULKAN_BUFFER_TYPE_GPU, + VULKAN_BUFFER_TYPE_UNIFORM, + VULKAN_BUFFER_TYPE_TRANSFER +} VulkanBufferType; + +struct VulkanBuffer +{ + VkBuffer buffer; + VkDeviceSize size; + VulkanMemoryUsedRegion *usedRegion; + + VulkanBufferType type; + Refresh_BufferUsageFlags usageFlags; + + SDL_atomic_t referenceCount; /* Tracks command buffer usage */ + + VulkanBufferHandle *handle; + + SDL_bool transitioned; + Uint8 markedForDestroy; /* so that defrag doesn't double-free */ +}; + +/* Buffer resources consist of multiple backing buffer handles so that data transfers + * can occur without blocking or the client having to manage extra resources. + * + * Cast from Refresh_Buffer or Refresh_TransferBuffer. + */ +struct VulkanBufferContainer +{ + VulkanBufferHandle *activeBufferHandle; + + /* These are all the buffer handles that have been used by this container. + * If the resource is bound and then updated with a cycle parameter, a new resource + * will be added to this list. + * These can be reused after they are submitted and command processing is complete. + */ + Uint32 bufferCapacity; + Uint32 bufferCount; + VulkanBufferHandle **bufferHandles; + + char *debugName; +}; + +/* Renderer Structure */ + +typedef struct QueueFamilyIndices +{ + Uint32 graphicsFamily; + Uint32 presentFamily; + Uint32 computeFamily; + Uint32 transferFamily; +} QueueFamilyIndices; + +typedef struct VulkanSampler +{ + VkSampler sampler; + SDL_atomic_t referenceCount; +} VulkanSampler; + +typedef struct VulkanShader +{ + VkShaderModule shaderModule; + const char *entryPointName; + SDL_atomic_t referenceCount; +} VulkanShader; + +typedef struct VulkanTextureHandle +{ + VulkanTexture *vulkanTexture; + VulkanTextureContainer *container; +} VulkanTextureHandle; + +/* Textures are made up of individual slices. + * This helps us barrier the resource efficiently. + */ +typedef struct VulkanTextureSlice +{ + VulkanTexture *parent; + Uint32 layer; + Uint32 level; + + SDL_atomic_t referenceCount; + + VkImageView view; + VulkanTextureHandle *msaaTexHandle; /* NULL if parent sample count is 1 or is depth target */ + + SDL_bool transitioned; /* used for layout tracking */ +} VulkanTextureSlice; + +struct VulkanTexture +{ + VulkanMemoryUsedRegion *usedRegion; + + VkImage image; + VkImageView view; + VkExtent2D dimensions; + + Uint8 is3D; + Uint8 isCube; + Uint8 isRenderTarget; + + Uint32 depth; + Uint32 layerCount; + Uint32 levelCount; + VkSampleCountFlagBits sampleCount; /* NOTE: This refers to the sample count of a render target pass using this texture, not the actual sample count of the texture */ + VkFormat format; + VkComponentMapping swizzle; + Refresh_TextureUsageFlags usageFlags; + VkImageAspectFlags aspectFlags; + + Uint32 sliceCount; + VulkanTextureSlice *slices; + + VulkanTextureHandle *handle; + + Uint8 markedForDestroy; /* so that defrag doesn't double-free */ +}; + +/* Texture resources consist of multiple backing texture handles so that data transfers + * can occur without blocking or the client having to manage extra resources. + * + * Cast from Refresh_Texture. + */ +struct VulkanTextureContainer +{ + VulkanTextureHandle *activeTextureHandle; + + /* These are all the texture handles that have been used by this container. + * If the resource is bound and then updated with CYCLE, a new resource + * will be added to this list. + * These can be reused after they are submitted and command processing is complete. + */ + Uint32 textureCapacity; + Uint32 textureCount; + VulkanTextureHandle **textureHandles; + + /* Swapchain images cannot be cycled */ + Uint8 canBeCycled; + + char *debugName; +}; + +typedef enum VulkanBufferUsageMode +{ + VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE, + VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION, + VULKAN_BUFFER_USAGE_MODE_VERTEX_READ, + VULKAN_BUFFER_USAGE_MODE_INDEX_READ, + VULKAN_BUFFER_USAGE_MODE_INDIRECT, + VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ, + VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ, + VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE, +} VulkanBufferUsageMode; + +typedef enum VulkanTextureUsageMode +{ + VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, + VULKAN_TEXTURE_USAGE_MODE_SAMPLER, + VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ, + VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ, + VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE, + VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT, + VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT, + VULKAN_TEXTURE_USAGE_MODE_PRESENT +} VulkanTextureUsageMode; + +typedef struct VulkanFramebuffer +{ + VkFramebuffer framebuffer; + SDL_atomic_t referenceCount; +} VulkanFramebuffer; + +typedef struct VulkanSwapchainData +{ + /* Window surface */ + VkSurfaceKHR surface; + + /* Swapchain for window surface */ + VkSwapchainKHR swapchain; + VkFormat format; + VkColorSpaceKHR colorSpace; + VkComponentMapping swapchainSwizzle; + VkPresentModeKHR presentMode; + + /* Swapchain images */ + VkExtent2D extent; + VulkanTextureContainer *textureContainers; /* use containers so that swapchain textures can use the same API as other textures */ + Uint32 imageCount; + + /* Synchronization primitives */ + VkSemaphore imageAvailableSemaphore[MAX_FRAMES_IN_FLIGHT]; + VkSemaphore renderFinishedSemaphore[MAX_FRAMES_IN_FLIGHT]; + VulkanFenceHandle *inFlightFences[MAX_FRAMES_IN_FLIGHT]; + + Uint32 frameCounter; +} VulkanSwapchainData; + +typedef struct WindowData +{ + SDL_Window *window; + Refresh_SwapchainComposition swapchainComposition; + Refresh_PresentMode presentMode; + VulkanSwapchainData *swapchainData; +} WindowData; + +typedef struct SwapchainSupportDetails +{ + VkSurfaceCapabilitiesKHR capabilities; + VkSurfaceFormatKHR *formats; + Uint32 formatsLength; + VkPresentModeKHR *presentModes; + Uint32 presentModesLength; +} SwapchainSupportDetails; + +typedef struct VulkanPresentData +{ + WindowData *windowData; + Uint32 swapchainImageIndex; +} VulkanPresentData; + +typedef struct VulkanUniformBuffer +{ + VulkanBufferContainer *bufferContainer; + + Uint32 drawOffset; + Uint32 offset; +} VulkanUniformBuffer; + +typedef struct VulkanDescriptorInfo +{ + VkDescriptorType descriptorType; + VkShaderStageFlagBits stageFlag; +} VulkanDescriptorInfo; + +typedef struct DescriptorSetPool +{ + SDL_mutex *lock; + + VkDescriptorSetLayout descriptorSetLayout; + + VulkanDescriptorInfo *descriptorInfos; + Uint32 descriptorInfoCount; + + /* This is actually a descriptor set and descriptor pool simultaneously */ + VkDescriptorPool *descriptorPools; + Uint32 descriptorPoolCount; + Uint32 nextPoolSize; + + /* We just manage a pool ourselves instead of freeing the sets */ + VkDescriptorSet *inactiveDescriptorSets; + Uint32 inactiveDescriptorSetCount; + Uint32 inactiveDescriptorSetCapacity; +} DescriptorSetPool; + +typedef struct VulkanGraphicsPipelineResourceLayout +{ + VkPipelineLayout pipelineLayout; + + /* + * Descriptor set layout is as follows: + * 0: vertex resources + * 1: vertex uniform buffers + * 2: fragment resources + * 3: fragment uniform buffers + */ + DescriptorSetPool descriptorSetPools[4]; + + Uint32 vertexSamplerCount; + Uint32 vertexStorageBufferCount; + Uint32 vertexStorageTextureCount; + Uint32 vertexUniformBufferCount; + + Uint32 fragmentSamplerCount; + Uint32 fragmentStorageBufferCount; + Uint32 fragmentStorageTextureCount; + Uint32 fragmentUniformBufferCount; +} VulkanGraphicsPipelineResourceLayout; + +typedef struct VulkanGraphicsPipeline +{ + VkPipeline pipeline; + Refresh_PrimitiveType primitiveType; + + VulkanGraphicsPipelineResourceLayout resourceLayout; + + VulkanShader *vertexShader; + VulkanShader *fragmentShader; + + SDL_atomic_t referenceCount; +} VulkanGraphicsPipeline; + +typedef struct VulkanComputePipelineResourceLayout +{ + VkPipelineLayout pipelineLayout; + + /* + * Descriptor set layout is as follows: + * 0: read-only textures, then read-only buffers + * 1: read-write textures, then read-write buffers + * 2: uniform buffers + */ + DescriptorSetPool descriptorSetPools[3]; + + Uint32 readOnlyStorageTextureCount; + Uint32 readOnlyStorageBufferCount; + Uint32 readWriteStorageTextureCount; + Uint32 readWriteStorageBufferCount; + Uint32 uniformBufferCount; +} VulkanComputePipelineResourceLayout; + +typedef struct VulkanComputePipeline +{ + VkPipeline pipeline; + + VulkanComputePipelineResourceLayout resourceLayout; + + VulkanShader *computeShader; + + SDL_atomic_t referenceCount; +} VulkanComputePipeline; + +typedef struct VulkanOcclusionQuery +{ + Uint32 index; +} VulkanOcclusionQuery; + +typedef struct RenderPassColorTargetDescription +{ + VkFormat format; + Refresh_Color clearColor; + Refresh_LoadOp loadOp; + Refresh_StoreOp storeOp; +} RenderPassColorTargetDescription; + +typedef struct RenderPassDepthStencilTargetDescription +{ + VkFormat format; + Refresh_LoadOp loadOp; + Refresh_StoreOp storeOp; + Refresh_LoadOp stencilLoadOp; + Refresh_StoreOp stencilStoreOp; +} RenderPassDepthStencilTargetDescription; + +typedef struct RenderPassHash +{ + RenderPassColorTargetDescription colorTargetDescriptions[MAX_COLOR_TARGET_BINDINGS]; + Uint32 colorAttachmentCount; + RenderPassDepthStencilTargetDescription depthStencilTargetDescription; + VkSampleCountFlagBits colorAttachmentSampleCount; +} RenderPassHash; + +typedef struct RenderPassHashMap +{ + RenderPassHash key; + VkRenderPass value; +} RenderPassHashMap; + +typedef struct RenderPassHashArray +{ + RenderPassHashMap *elements; + Sint32 count; + Sint32 capacity; +} RenderPassHashArray; + +static inline Uint8 RenderPassHash_Compare( + RenderPassHash *a, + RenderPassHash *b +) { + Uint32 i; + + if (a->colorAttachmentCount != b->colorAttachmentCount) + { + return 0; + } + + if (a->colorAttachmentSampleCount != b->colorAttachmentSampleCount) + { + return 0; + } + + for (i = 0; i < a->colorAttachmentCount; i += 1) + { + if (a->colorTargetDescriptions[i].format != b->colorTargetDescriptions[i].format) + { + return 0; + } + + if ( a->colorTargetDescriptions[i].clearColor.r != b->colorTargetDescriptions[i].clearColor.r || + a->colorTargetDescriptions[i].clearColor.g != b->colorTargetDescriptions[i].clearColor.g || + a->colorTargetDescriptions[i].clearColor.b != b->colorTargetDescriptions[i].clearColor.b || + a->colorTargetDescriptions[i].clearColor.a != b->colorTargetDescriptions[i].clearColor.a ) + { + return 0; + } + + if (a->colorTargetDescriptions[i].loadOp != b->colorTargetDescriptions[i].loadOp) + { + return 0; + } + + if (a->colorTargetDescriptions[i].storeOp != b->colorTargetDescriptions[i].storeOp) + { + return 0; + } + } + + if (a->depthStencilTargetDescription.format != b->depthStencilTargetDescription.format) + { + return 0; + } + + if (a->depthStencilTargetDescription.loadOp != b->depthStencilTargetDescription.loadOp) + { + return 0; + } + + if (a->depthStencilTargetDescription.storeOp != b->depthStencilTargetDescription.storeOp) + { + return 0; + } + + if (a->depthStencilTargetDescription.stencilLoadOp != b->depthStencilTargetDescription.stencilLoadOp) + { + return 0; + } + + if (a->depthStencilTargetDescription.stencilStoreOp != b->depthStencilTargetDescription.stencilStoreOp) + { + return 0; + } + + return 1; +} + +static inline VkRenderPass RenderPassHashArray_Fetch( + RenderPassHashArray *arr, + RenderPassHash *key +) { + Sint32 i; + + for (i = 0; i < arr->count; i += 1) + { + RenderPassHash *e = &arr->elements[i].key; + + if (RenderPassHash_Compare(e, key)) + { + return arr->elements[i].value; + } + } + + return VK_NULL_HANDLE; +} + +static inline void RenderPassHashArray_Insert( + RenderPassHashArray *arr, + RenderPassHash key, + VkRenderPass value +) { + RenderPassHashMap map; + + map.key = key; + map.value = value; + + EXPAND_ELEMENTS_IF_NEEDED(arr, 4, RenderPassHashMap) + + arr->elements[arr->count] = map; + arr->count += 1; +} + +typedef struct FramebufferHash +{ + VkImageView colorAttachmentViews[MAX_COLOR_TARGET_BINDINGS]; + VkImageView colorMultiSampleAttachmentViews[MAX_COLOR_TARGET_BINDINGS]; + Uint32 colorAttachmentCount; + VkImageView depthStencilAttachmentView; + Uint32 width; + Uint32 height; +} FramebufferHash; + +typedef struct FramebufferHashMap +{ + FramebufferHash key; + VulkanFramebuffer *value; +} FramebufferHashMap; + +typedef struct FramebufferHashArray +{ + FramebufferHashMap *elements; + Sint32 count; + Sint32 capacity; +} FramebufferHashArray; + +static inline Uint8 FramebufferHash_Compare( + FramebufferHash *a, + FramebufferHash *b +) { + Uint32 i; + + if (a->colorAttachmentCount != b->colorAttachmentCount) + { + return 0; + } + + for (i = 0; i < a->colorAttachmentCount; i += 1) + { + if (a->colorAttachmentViews[i] != b->colorAttachmentViews[i]) + { + return 0; + } + + if (a->colorMultiSampleAttachmentViews[i] != b->colorMultiSampleAttachmentViews[i]) + { + return 0; + } + } + + if (a->depthStencilAttachmentView != b->depthStencilAttachmentView) + { + return 0; + } + + if (a->width != b->width) + { + return 0; + } + + if (a->height != b->height) + { + return 0; + } + + return 1; +} + +static inline VulkanFramebuffer* FramebufferHashArray_Fetch( + FramebufferHashArray *arr, + FramebufferHash *key +) { + Sint32 i; + + for (i = 0; i < arr->count; i += 1) + { + FramebufferHash *e = &arr->elements[i].key; + if (FramebufferHash_Compare(e, key)) + { + return arr->elements[i].value; + } + } + + return VK_NULL_HANDLE; +} + +static inline void FramebufferHashArray_Insert( + FramebufferHashArray *arr, + FramebufferHash key, + VulkanFramebuffer *value +) { + FramebufferHashMap map; + map.key = key; + map.value = value; + + EXPAND_ELEMENTS_IF_NEEDED(arr, 4, FramebufferHashMap) + + arr->elements[arr->count] = map; + arr->count += 1; +} + +static inline void FramebufferHashArray_Remove( + FramebufferHashArray *arr, + Uint32 index +) { + if (index != arr->count - 1) + { + arr->elements[index] = arr->elements[arr->count - 1]; + } + + arr->count -= 1; +} + +/* Command structures */ + +typedef struct DescriptorSetData +{ + DescriptorSetPool *descriptorSetPool; + VkDescriptorSet descriptorSet; +} DescriptorSetData; + +typedef struct VulkanFencePool +{ + SDL_mutex *lock; + + VulkanFenceHandle **availableFences; + Uint32 availableFenceCount; + Uint32 availableFenceCapacity; +} VulkanFencePool; + +typedef struct VulkanCommandPool VulkanCommandPool; + +typedef struct VulkanRenderer VulkanRenderer; + +typedef struct VulkanCommandBuffer +{ + CommandBufferCommonHeader common; + VulkanRenderer *renderer; + + VkCommandBuffer commandBuffer; + VulkanCommandPool *commandPool; + + VulkanPresentData *presentDatas; + Uint32 presentDataCount; + Uint32 presentDataCapacity; + + VkSemaphore *waitSemaphores; + Uint32 waitSemaphoreCount; + Uint32 waitSemaphoreCapacity; + + VkSemaphore *signalSemaphores; + Uint32 signalSemaphoreCount; + Uint32 signalSemaphoreCapacity; + + VulkanComputePipeline *currentComputePipeline; + VulkanGraphicsPipeline *currentGraphicsPipeline; + + /* Keep track of resources transitioned away from their default state to barrier them on pass end */ + + VulkanTextureSlice *colorAttachmentSlices[MAX_COLOR_TARGET_BINDINGS]; + Uint32 colorAttachmentSliceCount; + + VulkanTextureSlice *depthStencilAttachmentSlice; /* may be NULL */ + + /* Viewport/scissor state */ + + VkViewport currentViewport; + VkRect2D currentScissor; + + /* Resource bind state */ + + SDL_bool needNewVertexResourceDescriptorSet; + SDL_bool needNewVertexUniformDescriptorSet; + SDL_bool needNewVertexUniformOffsets; + SDL_bool needNewFragmentResourceDescriptorSet; + SDL_bool needNewFragmentUniformDescriptorSet; + SDL_bool needNewFragmentUniformOffsets; + + SDL_bool needNewComputeReadOnlyDescriptorSet; + SDL_bool needNewComputeReadWriteDescriptorSet; + SDL_bool needNewComputeUniformDescriptorSet; + SDL_bool needNewComputeUniformOffsets; + + VkDescriptorSet vertexResourceDescriptorSet; + VkDescriptorSet vertexUniformDescriptorSet; + VkDescriptorSet fragmentResourceDescriptorSet; + VkDescriptorSet fragmentUniformDescriptorSet; + + VkDescriptorSet computeReadOnlyDescriptorSet; + VkDescriptorSet computeReadWriteDescriptorSet; + VkDescriptorSet computeUniformDescriptorSet; + + DescriptorSetData *boundDescriptorSetDatas; + Uint32 boundDescriptorSetDataCount; + Uint32 boundDescriptorSetDataCapacity; + + VulkanTexture *vertexSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE]; + VulkanSampler *vertexSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE]; + VulkanTextureSlice *vertexStorageTextureSlices[MAX_STORAGE_TEXTURES_PER_STAGE]; + VulkanBuffer *vertexStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE]; + + VulkanTexture *fragmentSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE]; + VulkanSampler *fragmentSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE]; + VulkanTextureSlice *fragmentStorageTextureSlices[MAX_STORAGE_TEXTURES_PER_STAGE]; + VulkanBuffer *fragmentStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE]; + + VulkanTextureSlice *readWriteComputeStorageTextureSlices[MAX_STORAGE_TEXTURES_PER_STAGE]; + VulkanBuffer *readWriteComputeStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE]; + + VulkanTextureSlice *readOnlyComputeStorageTextureSlices[MAX_STORAGE_TEXTURES_PER_STAGE]; + VulkanBuffer *readOnlyComputeStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE]; + + /* Uniform buffers */ + + VulkanUniformBuffer *vertexUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; + VulkanUniformBuffer *fragmentUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; + VulkanUniformBuffer *computeUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; + + Uint32 initializedVertexUniformBufferCount; + Uint32 initializedFragmentUniformBufferCount; + Uint32 initializedComputeUniformBufferCount; + + /* Track used resources */ + + VulkanBuffer **usedBuffers; + Uint32 usedBufferCount; + Uint32 usedBufferCapacity; + + VulkanTextureSlice **usedTextureSlices; + Uint32 usedTextureSliceCount; + Uint32 usedTextureSliceCapacity; + + VulkanSampler **usedSamplers; + Uint32 usedSamplerCount; + Uint32 usedSamplerCapacity; + + VulkanGraphicsPipeline **usedGraphicsPipelines; + Uint32 usedGraphicsPipelineCount; + Uint32 usedGraphicsPipelineCapacity; + + VulkanComputePipeline **usedComputePipelines; + Uint32 usedComputePipelineCount; + Uint32 usedComputePipelineCapacity; + + VulkanFramebuffer **usedFramebuffers; + Uint32 usedFramebufferCount; + Uint32 usedFramebufferCapacity; + + VulkanFenceHandle *inFlightFence; + Uint8 autoReleaseFence; + + Uint8 isDefrag; /* Whether this CB was created for defragging */ +} VulkanCommandBuffer; + +struct VulkanCommandPool +{ + SDL_threadID threadID; + VkCommandPool commandPool; + + VulkanCommandBuffer **inactiveCommandBuffers; + Uint32 inactiveCommandBufferCapacity; + Uint32 inactiveCommandBufferCount; +}; + +#define NUM_COMMAND_POOL_BUCKETS 1031 + +typedef struct CommandPoolHash +{ + SDL_threadID threadID; +} CommandPoolHash; + +typedef struct CommandPoolHashMap +{ + CommandPoolHash key; + VulkanCommandPool *value; +} CommandPoolHashMap; + +typedef struct CommandPoolHashArray +{ + CommandPoolHashMap *elements; + Uint32 count; + Uint32 capacity; +} CommandPoolHashArray; + +typedef struct CommandPoolHashTable +{ + CommandPoolHashArray buckets[NUM_COMMAND_POOL_BUCKETS]; +} CommandPoolHashTable; + +static inline uint64_t CommandPoolHashTable_GetHashCode(CommandPoolHash key) +{ + const uint64_t HASH_FACTOR = 97; + uint64_t result = 1; + result = result * HASH_FACTOR + (uint64_t) key.threadID; + return result; +} + +static inline VulkanCommandPool* CommandPoolHashTable_Fetch( + CommandPoolHashTable *table, + CommandPoolHash key +) { + Uint32 i; + uint64_t hashcode = CommandPoolHashTable_GetHashCode(key); + CommandPoolHashArray *arr = &table->buckets[hashcode % NUM_COMMAND_POOL_BUCKETS]; + + for (i = 0; i < arr->count; i += 1) + { + const CommandPoolHash *e = &arr->elements[i].key; + if (key.threadID == e->threadID) + { + return arr->elements[i].value; + } + } + + return NULL; +} + +static inline void CommandPoolHashTable_Insert( + CommandPoolHashTable *table, + CommandPoolHash key, + VulkanCommandPool *value +) { + uint64_t hashcode = CommandPoolHashTable_GetHashCode(key); + CommandPoolHashArray *arr = &table->buckets[hashcode % NUM_COMMAND_POOL_BUCKETS]; + + CommandPoolHashMap map; + map.key = key; + map.value = value; + + EXPAND_ELEMENTS_IF_NEEDED(arr, 4, CommandPoolHashMap) + + arr->elements[arr->count] = map; + arr->count += 1; +} + +/* Context */ + +struct VulkanRenderer +{ + VkInstance instance; + VkPhysicalDevice physicalDevice; + VkPhysicalDeviceProperties2 physicalDeviceProperties; + VkPhysicalDeviceDriverPropertiesKHR physicalDeviceDriverProperties; + VkDevice logicalDevice; + Uint8 integratedMemoryNotification; + Uint8 outOfDeviceLocalMemoryWarning; + Uint8 outofBARMemoryWarning; + + Uint8 supportsDebugUtils; + Uint8 debugMode; + VulkanExtensions supports; + + VulkanMemoryAllocator *memoryAllocator; + VkPhysicalDeviceMemoryProperties memoryProperties; + + WindowData **claimedWindows; + Uint32 claimedWindowCount; + Uint32 claimedWindowCapacity; + + Uint32 queueFamilyIndex; + VkQueue unifiedQueue; + + VulkanCommandBuffer **submittedCommandBuffers; + Uint32 submittedCommandBufferCount; + Uint32 submittedCommandBufferCapacity; + + VulkanFencePool fencePool; + + CommandPoolHashTable commandPoolHashTable; + RenderPassHashArray renderPassHashArray; + FramebufferHashArray framebufferHashArray; + + Uint32 minUBOAlignment; + + /* Some drivers don't support D16 for some reason. Fun! */ + VkFormat D16Format; + VkFormat D16S8Format; + + /* Queries */ + VkQueryPool queryPool; + Sint8 freeQueryIndexStack[MAX_QUERIES]; + Sint8 freeQueryIndexStackHead; + + /* Deferred resource destruction */ + + VulkanTexture **texturesToDestroy; + Uint32 texturesToDestroyCount; + Uint32 texturesToDestroyCapacity; + + VulkanBuffer **buffersToDestroy; + Uint32 buffersToDestroyCount; + Uint32 buffersToDestroyCapacity; + + VulkanSampler **samplersToDestroy; + Uint32 samplersToDestroyCount; + Uint32 samplersToDestroyCapacity; + + VulkanGraphicsPipeline **graphicsPipelinesToDestroy; + Uint32 graphicsPipelinesToDestroyCount; + Uint32 graphicsPipelinesToDestroyCapacity; + + VulkanComputePipeline **computePipelinesToDestroy; + Uint32 computePipelinesToDestroyCount; + Uint32 computePipelinesToDestroyCapacity; + + VulkanShader **shadersToDestroy; + Uint32 shadersToDestroyCount; + Uint32 shadersToDestroyCapacity; + + VulkanFramebuffer **framebuffersToDestroy; + Uint32 framebuffersToDestroyCount; + Uint32 framebuffersToDestroyCapacity; + + SDL_mutex *allocatorLock; + SDL_mutex *disposeLock; + SDL_mutex *submitLock; + SDL_mutex *acquireCommandBufferLock; + SDL_mutex *renderPassFetchLock; + SDL_mutex *framebufferFetchLock; + SDL_mutex *queryLock; + + Uint8 defragInProgress; + + VulkanMemoryAllocation **allocationsToDefrag; + Uint32 allocationsToDefragCount; + Uint32 allocationsToDefragCapacity; + + /* Support checks */ + + SDL_bool supportsPreciseOcclusionQueries; + +#define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \ + vkfntype_##func func; + #define VULKAN_DEVICE_FUNCTION(ext, ret, func, params) \ + vkfntype_##func func; + #include "Refresh_vulkan_vkfuncs.h" +}; + +/* Forward declarations */ + +static Uint8 VULKAN_INTERNAL_DefragmentMemory(VulkanRenderer *renderer); +static void VULKAN_INTERNAL_BeginCommandBuffer(VulkanRenderer *renderer, VulkanCommandBuffer *commandBuffer); +static void VULKAN_UnclaimWindow(Refresh_Renderer *driverData, SDL_Window *window); +static void VULKAN_Wait(Refresh_Renderer *driverData); +static void VULKAN_WaitForFences(Refresh_Renderer *driverData, SDL_bool waitAll, Uint32 fenceCount, Refresh_Fence **pFences); +static void VULKAN_Submit(Refresh_CommandBuffer *commandBuffer); +static VulkanTextureSlice* VULKAN_INTERNAL_FetchTextureSlice(VulkanTexture* texture, Uint32 layer, Uint32 level); +static VulkanTexture* VULKAN_INTERNAL_CreateTexture( + VulkanRenderer *renderer, + Uint32 width, + Uint32 height, + Uint32 depth, + Uint32 isCube, + Uint32 layerCount, + Uint32 levelCount, + VkSampleCountFlagBits sampleCount, + VkFormat format, + VkComponentMapping swizzle, + VkImageAspectFlags aspectMask, + Refresh_TextureUsageFlags textureUsageFlags +); + +/* Error Handling */ + +static inline const char* VkErrorMessages(VkResult code) +{ + #define ERR_TO_STR(e) \ + case e: return #e; + switch (code) + { + ERR_TO_STR(VK_ERROR_OUT_OF_HOST_MEMORY) + ERR_TO_STR(VK_ERROR_OUT_OF_DEVICE_MEMORY) + ERR_TO_STR(VK_ERROR_FRAGMENTED_POOL) + ERR_TO_STR(VK_ERROR_OUT_OF_POOL_MEMORY) + ERR_TO_STR(VK_ERROR_INITIALIZATION_FAILED) + ERR_TO_STR(VK_ERROR_LAYER_NOT_PRESENT) + ERR_TO_STR(VK_ERROR_EXTENSION_NOT_PRESENT) + ERR_TO_STR(VK_ERROR_FEATURE_NOT_PRESENT) + ERR_TO_STR(VK_ERROR_TOO_MANY_OBJECTS) + ERR_TO_STR(VK_ERROR_DEVICE_LOST) + ERR_TO_STR(VK_ERROR_INCOMPATIBLE_DRIVER) + ERR_TO_STR(VK_ERROR_OUT_OF_DATE_KHR) + ERR_TO_STR(VK_ERROR_SURFACE_LOST_KHR) + ERR_TO_STR(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT) + ERR_TO_STR(VK_SUBOPTIMAL_KHR) + default: return "Unhandled VkResult!"; + } + #undef ERR_TO_STR +} + +static inline void LogVulkanResultAsError( + const char* vulkanFunctionName, + VkResult result +) { + if (result != VK_SUCCESS) + { + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "%s: %s", + vulkanFunctionName, + VkErrorMessages(result) + ); + } +} + +#define VULKAN_ERROR_CHECK(res, fn, ret) \ + if (res != VK_SUCCESS) \ + { \ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s %s", #fn, VkErrorMessages(res)); \ + return ret; \ + } + +/* Utility */ + +static inline SDL_bool VULKAN_INTERNAL_IsVulkanDepthFormat(VkFormat format) +{ + /* FIXME: Can we refactor and use the regular IsDepthFormat for this? */ + return ( + format == SDLToVK_SurfaceFormat[REFRESH_TEXTUREFORMAT_D16_UNORM] || + format == SDLToVK_SurfaceFormat[REFRESH_TEXTUREFORMAT_D24_UNORM] || + format == SDLToVK_SurfaceFormat[REFRESH_TEXTUREFORMAT_D24_UNORM_S8_UINT] || + format == SDLToVK_SurfaceFormat[REFRESH_TEXTUREFORMAT_D32_SFLOAT] || + format == SDLToVK_SurfaceFormat[REFRESH_TEXTUREFORMAT_D32_SFLOAT_S8_UINT] + ); +} + +static inline VkSampleCountFlagBits VULKAN_INTERNAL_GetMaxMultiSampleCount( + VulkanRenderer *renderer, + VkSampleCountFlagBits multiSampleCount +) { + VkSampleCountFlags flags = renderer->physicalDeviceProperties.properties.limits.framebufferColorSampleCounts; + VkSampleCountFlagBits maxSupported = VK_SAMPLE_COUNT_1_BIT; + + if (flags & VK_SAMPLE_COUNT_8_BIT) + { + maxSupported = VK_SAMPLE_COUNT_8_BIT; + } + else if (flags & VK_SAMPLE_COUNT_4_BIT) + { + maxSupported = VK_SAMPLE_COUNT_4_BIT; + } + else if (flags & VK_SAMPLE_COUNT_2_BIT) + { + maxSupported = VK_SAMPLE_COUNT_2_BIT; + } + + return SDL_min(multiSampleCount, maxSupported); +} + +static inline SDL_bool BGRToRGBSwapchainFormat( + VkFormat format, + VkFormat *outputFormat +) { + switch (format) + { + case VK_FORMAT_B8G8R8A8_UNORM: + *outputFormat = VK_FORMAT_R8G8B8A8_UNORM; + return SDL_TRUE; + + case VK_FORMAT_A2B10G10R10_UNORM_PACK32: + *outputFormat = VK_FORMAT_A2R10G10B10_UNORM_PACK32; + return SDL_TRUE; + + case VK_FORMAT_B8G8R8A8_SRGB: + *outputFormat = VK_FORMAT_R8G8B8A8_SRGB; + return SDL_TRUE; + + default: + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No valid fallback equivalent for this format exists!"); + *outputFormat = VK_FORMAT_R8G8B8A8_UNORM; + return SDL_FALSE; + } +} + +/* Memory Management */ + +/* Vulkan: Memory Allocation */ + +static inline VkDeviceSize VULKAN_INTERNAL_NextHighestAlignment( + VkDeviceSize n, + VkDeviceSize align +) { + return align * ((n + align - 1) / align); +} + +static inline Uint32 VULKAN_INTERNAL_NextHighestAlignment32( + Uint32 n, + Uint32 align +) { + return align * ((n + align - 1) / align); +} + +static void VULKAN_INTERNAL_MakeMemoryUnavailable( + VulkanRenderer* renderer, + VulkanMemoryAllocation *allocation +) { + Uint32 i, j; + VulkanMemoryFreeRegion *freeRegion; + + allocation->availableForAllocation = 0; + + for (i = 0; i < allocation->freeRegionCount; i += 1) + { + freeRegion = allocation->freeRegions[i]; + + /* close the gap in the sorted list */ + if (allocation->allocator->sortedFreeRegionCount > 1) + { + for (j = freeRegion->sortedIndex; j < allocation->allocator->sortedFreeRegionCount - 1; j += 1) + { + allocation->allocator->sortedFreeRegions[j] = + allocation->allocator->sortedFreeRegions[j + 1]; + + allocation->allocator->sortedFreeRegions[j]->sortedIndex = j; + } + } + + allocation->allocator->sortedFreeRegionCount -= 1; + } +} + +static void VULKAN_INTERNAL_MarkAllocationsForDefrag( + VulkanRenderer *renderer +) { + Uint32 memoryType, allocationIndex; + VulkanMemorySubAllocator *currentAllocator; + + for (memoryType = 0; memoryType < VK_MAX_MEMORY_TYPES; memoryType += 1) + { + currentAllocator = &renderer->memoryAllocator->subAllocators[memoryType]; + + for (allocationIndex = 0; allocationIndex < currentAllocator->allocationCount; allocationIndex += 1) + { + if (currentAllocator->allocations[allocationIndex]->availableForAllocation == 1) + { + if (currentAllocator->allocations[allocationIndex]->freeRegionCount > 1) + { + EXPAND_ARRAY_IF_NEEDED( + renderer->allocationsToDefrag, + VulkanMemoryAllocation*, + renderer->allocationsToDefragCount + 1, + renderer->allocationsToDefragCapacity, + renderer->allocationsToDefragCapacity * 2 + ); + + renderer->allocationsToDefrag[renderer->allocationsToDefragCount] = + currentAllocator->allocations[allocationIndex]; + + renderer->allocationsToDefragCount += 1; + + VULKAN_INTERNAL_MakeMemoryUnavailable( + renderer, + currentAllocator->allocations[allocationIndex] + ); + } + } + } + } +} + +static void VULKAN_INTERNAL_RemoveMemoryFreeRegion( + VulkanRenderer *renderer, + VulkanMemoryFreeRegion *freeRegion +) { + Uint32 i; + + SDL_LockMutex(renderer->allocatorLock); + + if (freeRegion->allocation->availableForAllocation) + { + /* close the gap in the sorted list */ + if (freeRegion->allocation->allocator->sortedFreeRegionCount > 1) + { + for (i = freeRegion->sortedIndex; i < freeRegion->allocation->allocator->sortedFreeRegionCount - 1; i += 1) + { + freeRegion->allocation->allocator->sortedFreeRegions[i] = + freeRegion->allocation->allocator->sortedFreeRegions[i + 1]; + + freeRegion->allocation->allocator->sortedFreeRegions[i]->sortedIndex = i; + } + } + + freeRegion->allocation->allocator->sortedFreeRegionCount -= 1; + } + + /* close the gap in the buffer list */ + if (freeRegion->allocation->freeRegionCount > 1 && freeRegion->allocationIndex != freeRegion->allocation->freeRegionCount - 1) + { + freeRegion->allocation->freeRegions[freeRegion->allocationIndex] = + freeRegion->allocation->freeRegions[freeRegion->allocation->freeRegionCount - 1]; + + freeRegion->allocation->freeRegions[freeRegion->allocationIndex]->allocationIndex = + freeRegion->allocationIndex; + } + + freeRegion->allocation->freeRegionCount -= 1; + + freeRegion->allocation->freeSpace -= freeRegion->size; + + SDL_free(freeRegion); + + SDL_UnlockMutex(renderer->allocatorLock); +} + +static void VULKAN_INTERNAL_NewMemoryFreeRegion( + VulkanRenderer *renderer, + VulkanMemoryAllocation *allocation, + VkDeviceSize offset, + VkDeviceSize size +) { + VulkanMemoryFreeRegion *newFreeRegion; + VkDeviceSize newOffset, newSize; + Sint32 insertionIndex = 0; + Sint32 i; + + SDL_LockMutex(renderer->allocatorLock); + + /* look for an adjacent region to merge */ + for (i = allocation->freeRegionCount - 1; i >= 0; i -= 1) + { + /* check left side */ + if (allocation->freeRegions[i]->offset + allocation->freeRegions[i]->size == offset) + { + newOffset = allocation->freeRegions[i]->offset; + newSize = allocation->freeRegions[i]->size + size; + + VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, allocation->freeRegions[i]); + VULKAN_INTERNAL_NewMemoryFreeRegion(renderer, allocation, newOffset, newSize); + + SDL_UnlockMutex(renderer->allocatorLock); + return; + } + + /* check right side */ + if (allocation->freeRegions[i]->offset == offset + size) + { + newOffset = offset; + newSize = allocation->freeRegions[i]->size + size; + + VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, allocation->freeRegions[i]); + VULKAN_INTERNAL_NewMemoryFreeRegion(renderer, allocation, newOffset, newSize); + + SDL_UnlockMutex(renderer->allocatorLock); + return; + } + } + + /* region is not contiguous with another free region, make a new one */ + allocation->freeRegionCount += 1; + if (allocation->freeRegionCount > allocation->freeRegionCapacity) + { + allocation->freeRegionCapacity *= 2; + allocation->freeRegions = SDL_realloc( + allocation->freeRegions, + sizeof(VulkanMemoryFreeRegion*) * allocation->freeRegionCapacity + ); + } + + newFreeRegion = SDL_malloc(sizeof(VulkanMemoryFreeRegion)); + newFreeRegion->offset = offset; + newFreeRegion->size = size; + newFreeRegion->allocation = allocation; + + allocation->freeSpace += size; + + allocation->freeRegions[allocation->freeRegionCount - 1] = newFreeRegion; + newFreeRegion->allocationIndex = allocation->freeRegionCount - 1; + + if (allocation->availableForAllocation) + { + for (i = 0; i < allocation->allocator->sortedFreeRegionCount; i += 1) + { + if (allocation->allocator->sortedFreeRegions[i]->size < size) + { + /* this is where the new region should go */ + break; + } + + insertionIndex += 1; + } + + if (allocation->allocator->sortedFreeRegionCount + 1 > allocation->allocator->sortedFreeRegionCapacity) + { + allocation->allocator->sortedFreeRegionCapacity *= 2; + allocation->allocator->sortedFreeRegions = SDL_realloc( + allocation->allocator->sortedFreeRegions, + sizeof(VulkanMemoryFreeRegion*) * allocation->allocator->sortedFreeRegionCapacity + ); + } + + /* perform insertion sort */ + if (allocation->allocator->sortedFreeRegionCount > 0 && insertionIndex != allocation->allocator->sortedFreeRegionCount) + { + for (i = allocation->allocator->sortedFreeRegionCount; i > insertionIndex && i > 0; i -= 1) + { + allocation->allocator->sortedFreeRegions[i] = allocation->allocator->sortedFreeRegions[i - 1]; + allocation->allocator->sortedFreeRegions[i]->sortedIndex = i; + } + } + + allocation->allocator->sortedFreeRegionCount += 1; + allocation->allocator->sortedFreeRegions[insertionIndex] = newFreeRegion; + newFreeRegion->sortedIndex = insertionIndex; + } + + SDL_UnlockMutex(renderer->allocatorLock); +} + +static VulkanMemoryUsedRegion* VULKAN_INTERNAL_NewMemoryUsedRegion( + VulkanRenderer *renderer, + VulkanMemoryAllocation *allocation, + VkDeviceSize offset, + VkDeviceSize size, + VkDeviceSize resourceOffset, + VkDeviceSize resourceSize, + VkDeviceSize alignment +) { + VulkanMemoryUsedRegion *memoryUsedRegion; + + SDL_LockMutex(renderer->allocatorLock); + + if (allocation->usedRegionCount == allocation->usedRegionCapacity) + { + allocation->usedRegionCapacity *= 2; + allocation->usedRegions = SDL_realloc( + allocation->usedRegions, + allocation->usedRegionCapacity * sizeof(VulkanMemoryUsedRegion*) + ); + } + + memoryUsedRegion = SDL_malloc(sizeof(VulkanMemoryUsedRegion)); + memoryUsedRegion->allocation = allocation; + memoryUsedRegion->offset = offset; + memoryUsedRegion->size = size; + memoryUsedRegion->resourceOffset = resourceOffset; + memoryUsedRegion->resourceSize = resourceSize; + memoryUsedRegion->alignment = alignment; + + allocation->usedSpace += size; + + allocation->usedRegions[allocation->usedRegionCount] = memoryUsedRegion; + allocation->usedRegionCount += 1; + + SDL_UnlockMutex(renderer->allocatorLock); + + return memoryUsedRegion; +} + +static void VULKAN_INTERNAL_RemoveMemoryUsedRegion( + VulkanRenderer *renderer, + VulkanMemoryUsedRegion *usedRegion +) { + Uint32 i; + + SDL_LockMutex(renderer->allocatorLock); + + for (i = 0; i < usedRegion->allocation->usedRegionCount; i += 1) + { + if (usedRegion->allocation->usedRegions[i] == usedRegion) + { + /* plug the hole */ + if (i != usedRegion->allocation->usedRegionCount - 1) + { + usedRegion->allocation->usedRegions[i] = usedRegion->allocation->usedRegions[usedRegion->allocation->usedRegionCount - 1]; + } + + break; + } + } + + usedRegion->allocation->usedSpace -= usedRegion->size; + + usedRegion->allocation->usedRegionCount -= 1; + + VULKAN_INTERNAL_NewMemoryFreeRegion( + renderer, + usedRegion->allocation, + usedRegion->offset, + usedRegion->size + ); + + SDL_free(usedRegion); + + SDL_UnlockMutex(renderer->allocatorLock); +} + +static Uint8 VULKAN_INTERNAL_FindMemoryType( + VulkanRenderer *renderer, + Uint32 typeFilter, + VkMemoryPropertyFlags requiredProperties, + VkMemoryPropertyFlags ignoredProperties, + Uint32 *memoryTypeIndex +) { + Uint32 i; + + for (i = *memoryTypeIndex; i < renderer->memoryProperties.memoryTypeCount; i += 1) + { + if ( (typeFilter & (1 << i)) && + (renderer->memoryProperties.memoryTypes[i].propertyFlags & requiredProperties) == requiredProperties && + (renderer->memoryProperties.memoryTypes[i].propertyFlags & ignoredProperties) == 0 ) + { + *memoryTypeIndex = i; + return 1; + } + } + + return 0; +} + +static Uint8 VULKAN_INTERNAL_FindBufferMemoryRequirements( + VulkanRenderer *renderer, + VkBuffer buffer, + VkMemoryPropertyFlags requiredMemoryProperties, + VkMemoryPropertyFlags ignoredMemoryProperties, + VkMemoryRequirements2KHR *pMemoryRequirements, + Uint32 *pMemoryTypeIndex +) { + VkBufferMemoryRequirementsInfo2KHR bufferRequirementsInfo; + bufferRequirementsInfo.sType = + VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR; + bufferRequirementsInfo.pNext = NULL; + bufferRequirementsInfo.buffer = buffer; + + renderer->vkGetBufferMemoryRequirements2KHR( + renderer->logicalDevice, + &bufferRequirementsInfo, + pMemoryRequirements + ); + + return VULKAN_INTERNAL_FindMemoryType( + renderer, + pMemoryRequirements->memoryRequirements.memoryTypeBits, + requiredMemoryProperties, + ignoredMemoryProperties, + pMemoryTypeIndex + ); +} + +static Uint8 VULKAN_INTERNAL_FindImageMemoryRequirements( + VulkanRenderer *renderer, + VkImage image, + VkMemoryPropertyFlags requiredMemoryPropertyFlags, + VkMemoryRequirements2KHR *pMemoryRequirements, + Uint32 *pMemoryTypeIndex +) { + VkImageMemoryRequirementsInfo2KHR imageRequirementsInfo; + imageRequirementsInfo.sType = + VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR; + imageRequirementsInfo.pNext = NULL; + imageRequirementsInfo.image = image; + + renderer->vkGetImageMemoryRequirements2KHR( + renderer->logicalDevice, + &imageRequirementsInfo, + pMemoryRequirements + ); + + return VULKAN_INTERNAL_FindMemoryType( + renderer, + pMemoryRequirements->memoryRequirements.memoryTypeBits, + requiredMemoryPropertyFlags, + 0, + pMemoryTypeIndex + ); +} + +static void VULKAN_INTERNAL_DeallocateMemory( + VulkanRenderer *renderer, + VulkanMemorySubAllocator *allocator, + Uint32 allocationIndex +) { + Uint32 i; + + VulkanMemoryAllocation *allocation = allocator->allocations[allocationIndex]; + + SDL_LockMutex(renderer->allocatorLock); + + /* If this allocation was marked for defrag, cancel that */ + for (i = 0; i < renderer->allocationsToDefragCount; i += 1) + { + if (allocation == renderer->allocationsToDefrag[i]) + { + renderer->allocationsToDefrag[i] = renderer->allocationsToDefrag[renderer->allocationsToDefragCount - 1]; + renderer->allocationsToDefragCount -= 1; + + break; + } + } + + for (i = 0; i < allocation->freeRegionCount; i += 1) + { + VULKAN_INTERNAL_RemoveMemoryFreeRegion( + renderer, + allocation->freeRegions[i] + ); + } + SDL_free(allocation->freeRegions); + + /* no need to iterate used regions because deallocate + * only happens when there are 0 used regions + */ + SDL_free(allocation->usedRegions); + + renderer->vkFreeMemory( + renderer->logicalDevice, + allocation->memory, + NULL + ); + + SDL_DestroyMutex(allocation->memoryLock); + SDL_free(allocation); + + if (allocationIndex != allocator->allocationCount - 1) + { + allocator->allocations[allocationIndex] = allocator->allocations[allocator->allocationCount - 1]; + } + + allocator->allocationCount -= 1; + + SDL_UnlockMutex(renderer->allocatorLock); +} + +static Uint8 VULKAN_INTERNAL_AllocateMemory( + VulkanRenderer *renderer, + VkBuffer buffer, + VkImage image, + Uint32 memoryTypeIndex, + VkDeviceSize allocationSize, + Uint8 isHostVisible, + VulkanMemoryAllocation **pMemoryAllocation) +{ + VulkanMemoryAllocation *allocation; + VulkanMemorySubAllocator *allocator = &renderer->memoryAllocator->subAllocators[memoryTypeIndex]; + VkMemoryAllocateInfo allocInfo; + VkResult result; + + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.pNext = NULL; + allocInfo.memoryTypeIndex = memoryTypeIndex; + allocInfo.allocationSize = allocationSize; + + allocation = SDL_malloc(sizeof(VulkanMemoryAllocation)); + allocation->size = allocationSize; + allocation->freeSpace = 0; /* added by FreeRegions */ + allocation->usedSpace = 0; /* added by UsedRegions */ + allocation->memoryLock = SDL_CreateMutex(); + + allocator->allocationCount += 1; + allocator->allocations = SDL_realloc( + allocator->allocations, + sizeof(VulkanMemoryAllocation*) * allocator->allocationCount + ); + + allocator->allocations[ + allocator->allocationCount - 1 + ] = allocation; + + allocInfo.pNext = NULL; + allocation->availableForAllocation = 1; + + allocation->usedRegions = SDL_malloc(sizeof(VulkanMemoryUsedRegion*)); + allocation->usedRegionCount = 0; + allocation->usedRegionCapacity = 1; + + allocation->freeRegions = SDL_malloc(sizeof(VulkanMemoryFreeRegion*)); + allocation->freeRegionCount = 0; + allocation->freeRegionCapacity = 1; + + allocation->allocator = allocator; + + result = renderer->vkAllocateMemory( + renderer->logicalDevice, + &allocInfo, + NULL, + &allocation->memory + ); + + if (result != VK_SUCCESS) + { + /* Uh oh, we couldn't allocate, time to clean up */ + SDL_free(allocation->freeRegions); + + allocator->allocationCount -= 1; + allocator->allocations = SDL_realloc( + allocator->allocations, + sizeof(VulkanMemoryAllocation*) * allocator->allocationCount + ); + + SDL_free(allocation); + + return 0; + } + + /* Persistent mapping for host-visible memory */ + if (isHostVisible) + { + result = renderer->vkMapMemory( + renderer->logicalDevice, + allocation->memory, + 0, + VK_WHOLE_SIZE, + 0, + (void**) &allocation->mapPointer + ); + VULKAN_ERROR_CHECK(result, vkMapMemory, 0) + } + else + { + allocation->mapPointer = NULL; + } + + VULKAN_INTERNAL_NewMemoryFreeRegion( + renderer, + allocation, + 0, + allocation->size + ); + + *pMemoryAllocation = allocation; + return 1; +} + +static Uint8 VULKAN_INTERNAL_BindBufferMemory( + VulkanRenderer *renderer, + VulkanMemoryUsedRegion *usedRegion, + VkDeviceSize alignedOffset, + VkBuffer buffer +) { + VkResult vulkanResult; + + SDL_LockMutex(usedRegion->allocation->memoryLock); + + vulkanResult = renderer->vkBindBufferMemory( + renderer->logicalDevice, + buffer, + usedRegion->allocation->memory, + alignedOffset + ); + + SDL_UnlockMutex(usedRegion->allocation->memoryLock); + + VULKAN_ERROR_CHECK(vulkanResult, vkBindBufferMemory, 0) + + return 1; +} + +static Uint8 VULKAN_INTERNAL_BindImageMemory( + VulkanRenderer *renderer, + VulkanMemoryUsedRegion *usedRegion, + VkDeviceSize alignedOffset, + VkImage image +) { + VkResult vulkanResult; + + SDL_LockMutex(usedRegion->allocation->memoryLock); + + vulkanResult = renderer->vkBindImageMemory( + renderer->logicalDevice, + image, + usedRegion->allocation->memory, + alignedOffset + ); + + SDL_UnlockMutex(usedRegion->allocation->memoryLock); + + VULKAN_ERROR_CHECK(vulkanResult, vkBindBufferMemory, 0) + + return 1; +} + +static Uint8 VULKAN_INTERNAL_BindResourceMemory( + VulkanRenderer* renderer, + Uint32 memoryTypeIndex, + VkMemoryRequirements2KHR* memoryRequirements, + VkDeviceSize resourceSize, /* may be different from requirements size! */ + VkBuffer buffer, /* may be VK_NULL_HANDLE */ + VkImage image, /* may be VK_NULL_HANDLE */ + VulkanMemoryUsedRegion** pMemoryUsedRegion +) { + VulkanMemoryAllocation *allocation; + VulkanMemorySubAllocator *allocator; + VulkanMemoryFreeRegion *region; + VulkanMemoryFreeRegion *selectedRegion; + VulkanMemoryUsedRegion *usedRegion; + + VkDeviceSize requiredSize, allocationSize; + VkDeviceSize alignedOffset; + Uint32 newRegionSize, newRegionOffset; + Uint8 isHostVisible, smallAllocation, allocationResult; + Sint32 i; + + isHostVisible = + (renderer->memoryProperties.memoryTypes[memoryTypeIndex].propertyFlags & + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; + + allocator = &renderer->memoryAllocator->subAllocators[memoryTypeIndex]; + requiredSize = memoryRequirements->memoryRequirements.size; + smallAllocation = requiredSize <= SMALL_ALLOCATION_THRESHOLD; + + if ( (buffer == VK_NULL_HANDLE && image == VK_NULL_HANDLE) || + (buffer != VK_NULL_HANDLE && image != VK_NULL_HANDLE) ) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "BindResourceMemory must be given either a VulkanBuffer or a VulkanTexture"); + return 0; + } + + SDL_LockMutex(renderer->allocatorLock); + + selectedRegion = NULL; + + for (i = allocator->sortedFreeRegionCount - 1; i >= 0; i -= 1) + { + region = allocator->sortedFreeRegions[i]; + + if (smallAllocation && region->allocation->size != SMALL_ALLOCATION_SIZE) + { + /* region is not in a small allocation */ + continue; + } + + if (!smallAllocation && region->allocation->size == SMALL_ALLOCATION_SIZE) + { + /* allocation is not small and current region is in a small allocation */ + continue; + } + + alignedOffset = VULKAN_INTERNAL_NextHighestAlignment( + region->offset, + memoryRequirements->memoryRequirements.alignment + ); + + if (alignedOffset + requiredSize <= region->offset + region->size) + { + selectedRegion = region; + break; + } + } + + if (selectedRegion != NULL) + { + region = selectedRegion; + allocation = region->allocation; + + usedRegion = VULKAN_INTERNAL_NewMemoryUsedRegion( + renderer, + allocation, + region->offset, + requiredSize + (alignedOffset - region->offset), + alignedOffset, + resourceSize, + memoryRequirements->memoryRequirements.alignment + ); + + usedRegion->isBuffer = buffer != VK_NULL_HANDLE; + + newRegionSize = region->size - ((alignedOffset - region->offset) + requiredSize); + newRegionOffset = alignedOffset + requiredSize; + + /* remove and add modified region to re-sort */ + VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, region); + + /* if size is 0, no need to re-insert */ + if (newRegionSize != 0) + { + VULKAN_INTERNAL_NewMemoryFreeRegion( + renderer, + allocation, + newRegionOffset, + newRegionSize + ); + } + + SDL_UnlockMutex(renderer->allocatorLock); + + if (buffer != VK_NULL_HANDLE) + { + if (!VULKAN_INTERNAL_BindBufferMemory( + renderer, + usedRegion, + alignedOffset, + buffer + )) { + VULKAN_INTERNAL_RemoveMemoryUsedRegion( + renderer, + usedRegion + ); + + return 0; + } + } + else if (image != VK_NULL_HANDLE) + { + if (!VULKAN_INTERNAL_BindImageMemory( + renderer, + usedRegion, + alignedOffset, + image + )) { + VULKAN_INTERNAL_RemoveMemoryUsedRegion( + renderer, + usedRegion + ); + + return 0; + } + } + + *pMemoryUsedRegion = usedRegion; + return 1; + } + + /* No suitable free regions exist, allocate a new memory region */ + if ( + renderer->allocationsToDefragCount == 0 && + !renderer->defragInProgress + ) { + /* Mark currently fragmented allocations for defrag */ + VULKAN_INTERNAL_MarkAllocationsForDefrag(renderer); + } + + if (requiredSize > SMALL_ALLOCATION_THRESHOLD) + { + /* allocate a page of required size aligned to LARGE_ALLOCATION_INCREMENT increments */ + allocationSize = + VULKAN_INTERNAL_NextHighestAlignment(requiredSize, LARGE_ALLOCATION_INCREMENT); + } + else + { + allocationSize = SMALL_ALLOCATION_SIZE; + } + + allocationResult = VULKAN_INTERNAL_AllocateMemory( + renderer, + buffer, + image, + memoryTypeIndex, + allocationSize, + isHostVisible, + &allocation + ); + + /* Uh oh, we're out of memory */ + if (allocationResult == 0) + { + SDL_UnlockMutex(renderer->allocatorLock); + + /* Responsibility of the caller to handle being out of memory */ + return 2; + } + + usedRegion = VULKAN_INTERNAL_NewMemoryUsedRegion( + renderer, + allocation, + 0, + requiredSize, + 0, + resourceSize, + memoryRequirements->memoryRequirements.alignment + ); + + usedRegion->isBuffer = buffer != VK_NULL_HANDLE; + + region = allocation->freeRegions[0]; + + newRegionOffset = region->offset + requiredSize; + newRegionSize = region->size - requiredSize; + + VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, region); + + if (newRegionSize != 0) + { + VULKAN_INTERNAL_NewMemoryFreeRegion( + renderer, + allocation, + newRegionOffset, + newRegionSize + ); + } + + SDL_UnlockMutex(renderer->allocatorLock); + + if (buffer != VK_NULL_HANDLE) + { + if (!VULKAN_INTERNAL_BindBufferMemory( + renderer, + usedRegion, + 0, + buffer + )) { + VULKAN_INTERNAL_RemoveMemoryUsedRegion( + renderer, + usedRegion + ); + + return 0; + } + } + else if (image != VK_NULL_HANDLE) + { + if (!VULKAN_INTERNAL_BindImageMemory( + renderer, + usedRegion, + 0, + image + )) { + VULKAN_INTERNAL_RemoveMemoryUsedRegion( + renderer, + usedRegion + ); + + return 0; + } + } + + *pMemoryUsedRegion = usedRegion; + return 1; +} + +static Uint8 VULKAN_INTERNAL_BindMemoryForImage( + VulkanRenderer* renderer, + VkImage image, + VulkanMemoryUsedRegion** usedRegion +) { + Uint8 bindResult = 0; + Uint32 memoryTypeIndex = 0; + VkMemoryPropertyFlags requiredMemoryPropertyFlags; + VkMemoryRequirements2KHR memoryRequirements = + { + VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR, + NULL + }; + + /* Prefer GPU allocation for textures */ + requiredMemoryPropertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + + while (VULKAN_INTERNAL_FindImageMemoryRequirements( + renderer, + image, + requiredMemoryPropertyFlags, + &memoryRequirements, + &memoryTypeIndex + )) { + bindResult = VULKAN_INTERNAL_BindResourceMemory( + renderer, + memoryTypeIndex, + &memoryRequirements, + memoryRequirements.memoryRequirements.size, + VK_NULL_HANDLE, + image, + usedRegion + ); + + if (bindResult == 1) + { + break; + } + else /* Bind failed, try the next device-local heap */ + { + memoryTypeIndex += 1; + } + } + + /* Bind _still_ failed, try again without device local */ + if (bindResult != 1) + { + memoryTypeIndex = 0; + requiredMemoryPropertyFlags = 0; + + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Out of device-local memory, allocating textures on host-local memory!"); + + while (VULKAN_INTERNAL_FindImageMemoryRequirements( + renderer, + image, + requiredMemoryPropertyFlags, + &memoryRequirements, + &memoryTypeIndex + )) { + bindResult = VULKAN_INTERNAL_BindResourceMemory( + renderer, + memoryTypeIndex, + &memoryRequirements, + memoryRequirements.memoryRequirements.size, + VK_NULL_HANDLE, + image, + usedRegion + ); + + if (bindResult == 1) + { + break; + } + else /* Bind failed, try the next heap */ + { + memoryTypeIndex += 1; + } + } + } + + return bindResult; +} + +static Uint8 VULKAN_INTERNAL_BindMemoryForBuffer( + VulkanRenderer* renderer, + VkBuffer buffer, + VkDeviceSize size, + VulkanBufferType type, + VulkanMemoryUsedRegion** usedRegion +) { + Uint8 bindResult = 0; + Uint32 memoryTypeIndex = 0; + VkMemoryPropertyFlags requiredMemoryPropertyFlags = 0; + VkMemoryPropertyFlags ignoredMemoryPropertyFlags = 0; + VkMemoryRequirements2KHR memoryRequirements = + { + VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR, + NULL + }; + + if (type == VULKAN_BUFFER_TYPE_GPU) + { + requiredMemoryPropertyFlags |= + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + else if (type == VULKAN_BUFFER_TYPE_UNIFORM) + { + requiredMemoryPropertyFlags |= + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + } + else if (type == VULKAN_BUFFER_TYPE_TRANSFER) + { + requiredMemoryPropertyFlags |= + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + + ignoredMemoryPropertyFlags |= + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized buffer type!"); + return 0; + } + + while (VULKAN_INTERNAL_FindBufferMemoryRequirements( + renderer, + buffer, + requiredMemoryPropertyFlags, + ignoredMemoryPropertyFlags, + &memoryRequirements, + &memoryTypeIndex + )) { + bindResult = VULKAN_INTERNAL_BindResourceMemory( + renderer, + memoryTypeIndex, + &memoryRequirements, + size, + buffer, + VK_NULL_HANDLE, + usedRegion + ); + + if (bindResult == 1) + { + break; + } + else /* Bind failed, try the next device-local heap */ + { + memoryTypeIndex += 1; + } + } + + /* Bind failed, try again without preferred flags */ + if (bindResult != 1) + { + memoryTypeIndex = 0; + requiredMemoryPropertyFlags = 0; + ignoredMemoryPropertyFlags = 0; + + if (type == VULKAN_BUFFER_TYPE_GPU) + { + if (!renderer->outOfDeviceLocalMemoryWarning) + { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Out of device-local memory, allocating buffers on host-local memory, expect degraded performance!"); + renderer->outOfDeviceLocalMemoryWarning = 1; + } + } + else if (type == VULKAN_BUFFER_TYPE_UNIFORM) + { + requiredMemoryPropertyFlags = + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + + if (!renderer->outofBARMemoryWarning) + { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Out of BAR memory, allocating uniform buffers on host-local memory, expect degraded performance!"); + renderer->outofBARMemoryWarning = 1; + } + } + else if (type == VULKAN_BUFFER_TYPE_TRANSFER) + { + requiredMemoryPropertyFlags = + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + + if (!renderer->integratedMemoryNotification) + { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Integrated memory detected, allocating TransferBuffers on device-local memory!"); + renderer->integratedMemoryNotification = 1; + } + } + + while (VULKAN_INTERNAL_FindBufferMemoryRequirements( + renderer, + buffer, + requiredMemoryPropertyFlags, + ignoredMemoryPropertyFlags, + &memoryRequirements, + &memoryTypeIndex + )) { + bindResult = VULKAN_INTERNAL_BindResourceMemory( + renderer, + memoryTypeIndex, + &memoryRequirements, + size, + buffer, + VK_NULL_HANDLE, + usedRegion + ); + + if (bindResult == 1) + { + break; + } + else /* Bind failed, try the next heap */ + { + memoryTypeIndex += 1; + } + } + } + + return bindResult; +} + +/* Resource tracking */ + +#define ADD_TO_ARRAY_UNIQUE(resource, type, array, count, capacity) \ + Uint32 i; \ + \ + for (i = 0; i < commandBuffer->count; i += 1) \ + { \ + if (commandBuffer->array[i] == resource) \ + { \ + return; \ + } \ + } \ + \ + if (commandBuffer->count == commandBuffer->capacity) \ + { \ + commandBuffer->capacity += 1; \ + commandBuffer->array = SDL_realloc( \ + commandBuffer->array, \ + commandBuffer->capacity * sizeof(type) \ + ); \ + } \ + commandBuffer->array[commandBuffer->count] = resource; \ + commandBuffer->count += 1; + +#define TRACK_RESOURCE(resource, type, array, count, capacity) \ + Uint32 i; \ + \ + for (i = 0; i < commandBuffer->count; i += 1) \ + { \ + if (commandBuffer->array[i] == resource) \ + { \ + return; \ + } \ + } \ + \ + if (commandBuffer->count == commandBuffer->capacity) \ + { \ + commandBuffer->capacity += 1; \ + commandBuffer->array = SDL_realloc( \ + commandBuffer->array, \ + commandBuffer->capacity * sizeof(type) \ + ); \ + } \ + commandBuffer->array[commandBuffer->count] = resource; \ + commandBuffer->count += 1; \ + SDL_AtomicIncRef(&resource->referenceCount); + +static void VULKAN_INTERNAL_TrackBuffer( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanBuffer *buffer +) { + TRACK_RESOURCE( + buffer, + VulkanBuffer*, + usedBuffers, + usedBufferCount, + usedBufferCapacity + ) +} + +static void VULKAN_INTERNAL_TrackTextureSlice( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanTextureSlice *textureSlice +) { + TRACK_RESOURCE( + textureSlice, + VulkanTextureSlice*, + usedTextureSlices, + usedTextureSliceCount, + usedTextureSliceCapacity + ) +} + +static void VULKAN_INTERNAL_TrackSampler( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanSampler *sampler +) { + TRACK_RESOURCE( + sampler, + VulkanSampler*, + usedSamplers, + usedSamplerCount, + usedSamplerCapacity + ) +} + +static void VULKAN_INTERNAL_TrackGraphicsPipeline( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanGraphicsPipeline *graphicsPipeline +) { + TRACK_RESOURCE( + graphicsPipeline, + VulkanGraphicsPipeline*, + usedGraphicsPipelines, + usedGraphicsPipelineCount, + usedGraphicsPipelineCapacity + ) +} + +static void VULKAN_INTERNAL_TrackComputePipeline( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanComputePipeline *computePipeline +) { + TRACK_RESOURCE( + computePipeline, + VulkanComputePipeline*, + usedComputePipelines, + usedComputePipelineCount, + usedComputePipelineCapacity + ) +} + +static void VULKAN_INTERNAL_TrackFramebuffer( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanFramebuffer *framebuffer +) { + TRACK_RESOURCE( + framebuffer, + VulkanFramebuffer*, + usedFramebuffers, + usedFramebufferCount, + usedFramebufferCapacity + ); +} + +#undef TRACK_RESOURCE + +/* Memory Barriers */ + +/* + * In Vulkan, we must manually synchronize operations that write to resources on the GPU + * so that read-after-write, write-after-read, and write-after-write hazards do not occur. + * Additionally, textures are required to be in specific layouts for specific use cases. + * Both of these tasks are accomplished with vkCmdPipelineBarrier. + * + * To insert the correct barriers, we keep track of "usage modes" for buffers and textures. + * These indicate the current usage of that resource on the command buffer. + * The transition from one usage mode to another indicates how the barrier should be constructed. + * + * Pipeline barriers cannot be inserted during a render pass, but they can be inserted + * during a compute or copy pass. + * + * This means that the "default" usage mode of any given resource should be that it should be + * ready for a graphics-read operation, because we cannot barrier during a render pass. + * In the case where a resource is only used in compute, its default usage mode can be compute-read. + * This strategy allows us to avoid expensive record keeping of command buffer/resource usage mode pairs, + * and it fully covers synchronization between all combinations of stages. + * + * In Upload and Copy functions, we transition the resource immediately before and after the copy command. + * + * When binding a resource for compute, we transition when the Bind functions are called. + * If a bind slot containing a resource is overwritten, we transition the resource in that slot back to its default. + * When EndComputePass is called we transition all bound resources back to their default state. + * + * When binding a texture as a render pass attachment, we transition the resource on BeginRenderPass + * and transition it back to its default on EndRenderPass. + * + * This strategy imposes certain limitations on resource usage flags. + * For example, a texture cannot have both the SAMPLER and GRAPHICS_STORAGE usage flags, + * because then it is imposible for the backend to infer which default usage mode the texture should use. + * + * Sync hazards can be detected by setting VK_KHRONOS_VALIDATION_VALIDATE_SYNC=1 when using validation layers. + */ + +static void VULKAN_INTERNAL_BufferMemoryBarrier( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanBufferUsageMode sourceUsageMode, + VulkanBufferUsageMode destinationUsageMode, + VulkanBuffer *buffer +) { + VkPipelineStageFlags srcStages = 0; + VkPipelineStageFlags dstStages = 0; + VkBufferMemoryBarrier memoryBarrier; + + memoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + memoryBarrier.pNext = NULL; + memoryBarrier.srcAccessMask = 0; + memoryBarrier.dstAccessMask = 0; + memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + memoryBarrier.buffer = buffer->buffer; + memoryBarrier.offset = 0; + memoryBarrier.size = buffer->size; + + if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE) + { + srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + } + else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION) + { + srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + } + else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_VERTEX_READ) + { + srcStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + } + else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_INDEX_READ) + { + srcStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_INDEX_READ_BIT; + } + else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_INDIRECT) + { + srcStages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + } + else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ) + { + srcStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + } + else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ) + { + srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + } + else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) + { + srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized buffer source barrier type!"); + return; + } + + if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE) + { + dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + } + else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION) + { + dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + } + else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_VERTEX_READ) + { + dstStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + } + else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_INDEX_READ) + { + dstStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_INDEX_READ_BIT; + } + else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_INDIRECT) + { + dstStages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + } + else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ) + { + dstStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + } + else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ) + { + dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + } + else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) + { + dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized buffer destination barrier type!"); + return; + } + + renderer->vkCmdPipelineBarrier( + commandBuffer->commandBuffer, + srcStages, + dstStages, + 0, + 0, + NULL, + 1, + &memoryBarrier, + 0, + NULL + ); + + buffer->transitioned = SDL_TRUE; +} + +static void VULKAN_INTERNAL_ImageMemoryBarrier( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanTextureUsageMode sourceUsageMode, + VulkanTextureUsageMode destinationUsageMode, + VulkanTextureSlice *textureSlice +) { + VkPipelineStageFlags srcStages = 0; + VkPipelineStageFlags dstStages = 0; + VkImageMemoryBarrier memoryBarrier; + + memoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + memoryBarrier.pNext = NULL; + memoryBarrier.srcAccessMask = 0; + memoryBarrier.dstAccessMask = 0; + memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + memoryBarrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED; + memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + memoryBarrier.image = textureSlice->parent->image; + memoryBarrier.subresourceRange.aspectMask = textureSlice->parent->aspectFlags; + memoryBarrier.subresourceRange.baseArrayLayer = textureSlice->layer; + memoryBarrier.subresourceRange.layerCount = 1; + memoryBarrier.subresourceRange.baseMipLevel = textureSlice->level; + memoryBarrier.subresourceRange.levelCount = 1; + + if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE) + { + srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + } + else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION) + { + srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + } + else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_SAMPLER) + { + srcStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ) + { + srcStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + } + else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ) + { + srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + } + else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) + { + srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + } + else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT) + { + srcStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT) + { + srcStages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + memoryBarrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized texture source barrier type!"); + return; + } + + if (!textureSlice->transitioned) + { + memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE) + { + dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + memoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + } + else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION) + { + dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + memoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + } + else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_SAMPLER) + { + dstStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + memoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ) + { + dstStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + memoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + } + else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ) + { + dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + memoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + } + else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) + { + dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + memoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + } + else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT) + { + dstStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + memoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT) + { + dstStages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + memoryBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + memoryBarrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_PRESENT) + { + dstStages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + memoryBarrier.dstAccessMask = 0; + memoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized texture destination barrier type!"); + return; + } + + renderer->vkCmdPipelineBarrier( + commandBuffer->commandBuffer, + srcStages, + dstStages, + 0, + 0, + NULL, + 0, + NULL, + 1, + &memoryBarrier + ); + + textureSlice->transitioned = SDL_TRUE; +} + +static VulkanBufferUsageMode VULKAN_INTERNAL_DefaultBufferUsageMode( + VulkanBuffer *buffer +) { + /* NOTE: order matters here! */ + + if (buffer->usageFlags & REFRESH_BUFFERUSAGE_VERTEX_BIT) + { + return VULKAN_BUFFER_USAGE_MODE_VERTEX_READ; + } + else if (buffer->usageFlags & REFRESH_BUFFERUSAGE_INDEX_BIT) + { + return VULKAN_BUFFER_USAGE_MODE_INDEX_READ; + } + else if (buffer->usageFlags & REFRESH_BUFFERUSAGE_INDIRECT_BIT) + { + return VULKAN_BUFFER_USAGE_MODE_INDIRECT; + } + else if (buffer->usageFlags & REFRESH_BUFFERUSAGE_GRAPHICS_STORAGE_READ_BIT) + { + return VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ; + } + else if (buffer->usageFlags & REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_READ_BIT) + { + return VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ; + } + else if (buffer->usageFlags & REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_WRITE_BIT) + { + return VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Buffer has no default usage mode!"); + return VULKAN_BUFFER_USAGE_MODE_VERTEX_READ; + } +} + +static VulkanTextureUsageMode VULKAN_INTERNAL_DefaultTextureUsageMode( + VulkanTexture *texture +) { + /* NOTE: order matters here! */ + /* NOTE: graphics storage bits and sampler bit are mutually exclusive! */ + + if (texture->usageFlags & REFRESH_TEXTUREUSAGE_SAMPLER_BIT) + { + return VULKAN_TEXTURE_USAGE_MODE_SAMPLER; + } + else if (texture->usageFlags & REFRESH_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT) + { + return VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ; + } + else if (texture->usageFlags & REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT) + { + return VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT; + } + else if (texture->usageFlags & REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) + { + return VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT; + } + else if (texture->usageFlags & REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT) + { + return VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ; + } + else if (texture->usageFlags & REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT) + { + return VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Texture has no default usage mode!"); + return VULKAN_TEXTURE_USAGE_MODE_SAMPLER; + } +} + +static void VULKAN_INTERNAL_BufferTransitionFromDefaultUsage( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanBufferUsageMode destinationUsageMode, + VulkanBuffer *buffer +) { + VULKAN_INTERNAL_BufferMemoryBarrier( + renderer, + commandBuffer, + VULKAN_INTERNAL_DefaultBufferUsageMode(buffer), + destinationUsageMode, + buffer + ); +} + +static void VULKAN_INTERNAL_BufferTransitionToDefaultUsage( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanBufferUsageMode sourceUsageMode, + VulkanBuffer *buffer +) { + VULKAN_INTERNAL_BufferMemoryBarrier( + renderer, + commandBuffer, + sourceUsageMode, + VULKAN_INTERNAL_DefaultBufferUsageMode(buffer), + buffer + ); +} + +static void VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanTextureUsageMode destinationUsageMode, + VulkanTextureSlice *textureSlice +) { + VULKAN_INTERNAL_ImageMemoryBarrier( + renderer, + commandBuffer, + VULKAN_INTERNAL_DefaultTextureUsageMode(textureSlice->parent), + destinationUsageMode, + textureSlice + ); +} + +static void VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanTextureUsageMode sourceUsageMode, + VulkanTextureSlice *textureSlice +) { + VULKAN_INTERNAL_ImageMemoryBarrier( + renderer, + commandBuffer, + sourceUsageMode, + VULKAN_INTERNAL_DefaultTextureUsageMode(textureSlice->parent), + textureSlice + ); +} + +/* Resource Disposal */ + +static void VULKAN_INTERNAL_ReleaseFramebuffer( + VulkanRenderer *renderer, + VulkanFramebuffer *framebuffer +) { + SDL_LockMutex(renderer->disposeLock); + + EXPAND_ARRAY_IF_NEEDED( + renderer->framebuffersToDestroy, + VulkanFramebuffer*, + renderer->framebuffersToDestroyCount + 1, + renderer->framebuffersToDestroyCapacity, + renderer->framebuffersToDestroyCapacity * 2 + ) + + renderer->framebuffersToDestroy[renderer->framebuffersToDestroyCount] = framebuffer; + renderer->framebuffersToDestroyCount += 1; + + SDL_UnlockMutex(renderer->disposeLock); +} + +static void VULKAN_INTERNAL_DestroyFramebuffer( + VulkanRenderer *renderer, + VulkanFramebuffer *framebuffer +) { + renderer->vkDestroyFramebuffer( + renderer->logicalDevice, + framebuffer->framebuffer, + NULL + ); + + SDL_free(framebuffer); +} + +static void VULKAN_INTERNAL_RemoveFramebuffersContainingView( + VulkanRenderer *renderer, + VkImageView view +) { + FramebufferHash *hash; + Sint32 i, j; + + SDL_LockMutex(renderer->framebufferFetchLock); + + for (i = renderer->framebufferHashArray.count - 1; i >= 0; i -= 1) + { + hash = &renderer->framebufferHashArray.elements[i].key; + + for (j = 0; j < hash->colorAttachmentCount; j += 1) + { + if (hash->colorAttachmentViews[j] == view) + { + /* FIXME: do we actually need to queue this? + * The framebuffer should not be in use once the associated texture is being destroyed + */ + VULKAN_INTERNAL_ReleaseFramebuffer( + renderer, + renderer->framebufferHashArray.elements[i].value + ); + + FramebufferHashArray_Remove( + &renderer->framebufferHashArray, + i + ); + + break; + } + } + } + + SDL_UnlockMutex(renderer->framebufferFetchLock); +} + +static void VULKAN_INTERNAL_DestroyTexture( + VulkanRenderer* renderer, + VulkanTexture* texture +) { + Uint32 sliceIndex; + + /* Clean up slices */ + for (sliceIndex = 0; sliceIndex < texture->sliceCount; sliceIndex += 1) + { + if (texture->isRenderTarget) + { + VULKAN_INTERNAL_RemoveFramebuffersContainingView( + renderer, + texture->slices[sliceIndex].view + ); + + if (texture->slices[sliceIndex].msaaTexHandle != NULL) + { + VULKAN_INTERNAL_DestroyTexture( + renderer, + texture->slices[sliceIndex].msaaTexHandle->vulkanTexture + ); + SDL_free(texture->slices[sliceIndex].msaaTexHandle); + } + } + + renderer->vkDestroyImageView( + renderer->logicalDevice, + texture->slices[sliceIndex].view, + NULL + ); + } + + SDL_free(texture->slices); + + renderer->vkDestroyImageView( + renderer->logicalDevice, + texture->view, + NULL + ); + + renderer->vkDestroyImage( + renderer->logicalDevice, + texture->image, + NULL + ); + + VULKAN_INTERNAL_RemoveMemoryUsedRegion( + renderer, + texture->usedRegion + ); + + SDL_free(texture); +} + +static void VULKAN_INTERNAL_DestroyBuffer( + VulkanRenderer* renderer, + VulkanBuffer* buffer +) { + renderer->vkDestroyBuffer( + renderer->logicalDevice, + buffer->buffer, + NULL + ); + + VULKAN_INTERNAL_RemoveMemoryUsedRegion( + renderer, + buffer->usedRegion + ); + + SDL_free(buffer); +} + +static void VULKAN_INTERNAL_DestroyBufferContainer( + VulkanRenderer *renderer, + VulkanBufferContainer *bufferContainer +) { + Uint32 i; + + SDL_LockMutex(renderer->disposeLock); + + for (i = 0; i < bufferContainer->bufferCount; i += 1) + { + VULKAN_INTERNAL_DestroyBuffer(renderer, bufferContainer->bufferHandles[i]->vulkanBuffer); + SDL_free(bufferContainer->bufferHandles[i]); + } + + /* Containers are just client handles, so we can free immediately */ + if (bufferContainer->debugName != NULL) + { + SDL_free(bufferContainer->debugName); + } + SDL_free(bufferContainer->bufferHandles); + SDL_free(bufferContainer); + + SDL_UnlockMutex(renderer->disposeLock); +} + +static void VULKAN_INTERNAL_DestroyCommandPool( + VulkanRenderer *renderer, + VulkanCommandPool *commandPool +) { + Uint32 i, j; + VulkanCommandBuffer* commandBuffer; + + renderer->vkDestroyCommandPool( + renderer->logicalDevice, + commandPool->commandPool, + NULL + ); + + for (i = 0; i < commandPool->inactiveCommandBufferCount; i += 1) + { + commandBuffer = commandPool->inactiveCommandBuffers[i]; + + for (j = 0; j < commandBuffer->initializedVertexUniformBufferCount; j += 1) + { + VULKAN_INTERNAL_DestroyBufferContainer( + renderer, + commandBuffer->vertexUniformBuffers[j]->bufferContainer + ); + + SDL_free(commandBuffer->vertexUniformBuffers[j]); + } + + for (j = 0; j < commandBuffer->initializedFragmentUniformBufferCount; j += 1) + { + VULKAN_INTERNAL_DestroyBufferContainer( + renderer, + commandBuffer->fragmentUniformBuffers[j]->bufferContainer + ); + SDL_free(commandBuffer->fragmentUniformBuffers[j]); + } + + for (j = 0; j < commandBuffer->initializedComputeUniformBufferCount; j += 1) + { + VULKAN_INTERNAL_DestroyBufferContainer( + renderer, + commandBuffer->computeUniformBuffers[j]->bufferContainer + ); + SDL_free(commandBuffer->computeUniformBuffers[j]); + } + + SDL_free(commandBuffer->presentDatas); + SDL_free(commandBuffer->waitSemaphores); + SDL_free(commandBuffer->signalSemaphores); + SDL_free(commandBuffer->boundDescriptorSetDatas); + SDL_free(commandBuffer->usedBuffers); + SDL_free(commandBuffer->usedTextureSlices); + SDL_free(commandBuffer->usedSamplers); + SDL_free(commandBuffer->usedGraphicsPipelines); + SDL_free(commandBuffer->usedComputePipelines); + SDL_free(commandBuffer->usedFramebuffers); + + SDL_free(commandBuffer); + } + + SDL_free(commandPool->inactiveCommandBuffers); + SDL_free(commandPool); +} + +static void VULKAN_INTERNAL_DestroyDescriptorSetPool( + VulkanRenderer *renderer, + DescriptorSetPool *pool +) { + Uint32 i; + + if (pool == NULL) + { + return; + } + + for (i = 0; i < pool->descriptorPoolCount; i += 1) + { + renderer->vkDestroyDescriptorPool( + renderer->logicalDevice, + pool->descriptorPools[i], + NULL + ); + } + + renderer->vkDestroyDescriptorSetLayout( + renderer->logicalDevice, + pool->descriptorSetLayout, + NULL + ); + + SDL_free(pool->descriptorInfos); + SDL_free(pool->descriptorPools); + SDL_free(pool->inactiveDescriptorSets); + SDL_DestroyMutex(pool->lock); +} + +static void VULKAN_INTERNAL_DestroyGraphicsPipeline( + VulkanRenderer *renderer, + VulkanGraphicsPipeline *graphicsPipeline +) { + Uint32 i; + + renderer->vkDestroyPipeline( + renderer->logicalDevice, + graphicsPipeline->pipeline, + NULL + ); + + renderer->vkDestroyPipelineLayout( + renderer->logicalDevice, + graphicsPipeline->resourceLayout.pipelineLayout, + NULL + ); + + for (i = 0; i < 4; i += 1) + { + VULKAN_INTERNAL_DestroyDescriptorSetPool( + renderer, + &graphicsPipeline->resourceLayout.descriptorSetPools[i] + ); + } + + (void)SDL_AtomicDecRef(&graphicsPipeline->vertexShader->referenceCount); + (void)SDL_AtomicDecRef(&graphicsPipeline->fragmentShader->referenceCount); + + SDL_free(graphicsPipeline); +} + +static void VULKAN_INTERNAL_DestroyComputePipeline( + VulkanRenderer *renderer, + VulkanComputePipeline *computePipeline +) { + Uint32 i; + + renderer->vkDestroyPipeline( + renderer->logicalDevice, + computePipeline->pipeline, + NULL + ); + + renderer->vkDestroyPipelineLayout( + renderer->logicalDevice, + computePipeline->resourceLayout.pipelineLayout, + NULL + ); + + for (i = 0; i < 3; i += 1) + { + VULKAN_INTERNAL_DestroyDescriptorSetPool( + renderer, + &computePipeline->resourceLayout.descriptorSetPools[i] + ); + } + + (void)SDL_AtomicDecRef(&computePipeline->computeShader->referenceCount); + + SDL_free(computePipeline); +} + +static void VULKAN_INTERNAL_DestroyShaderModule( + VulkanRenderer *renderer, + VulkanShader *vulkanShaderModule +) { + renderer->vkDestroyShaderModule( + renderer->logicalDevice, + vulkanShaderModule->shaderModule, + NULL + ); + + SDL_free(vulkanShaderModule); +} + +static void VULKAN_INTERNAL_DestroySampler( + VulkanRenderer *renderer, + VulkanSampler *vulkanSampler +) { + renderer->vkDestroySampler( + renderer->logicalDevice, + vulkanSampler->sampler, + NULL + ); + + SDL_free(vulkanSampler); +} + +static void VULKAN_INTERNAL_DestroySwapchain( + VulkanRenderer* renderer, + WindowData *windowData +) { + Uint32 i; + VulkanSwapchainData *swapchainData; + + if (windowData == NULL) + { + return; + } + + swapchainData = windowData->swapchainData; + + if (swapchainData == NULL) + { + return; + } + + for (i = 0; i < swapchainData->imageCount; i += 1) + { + renderer->vkDestroyImageView( + renderer->logicalDevice, + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->slices[0].view, + NULL + ); + + SDL_free(swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->slices); + + renderer->vkDestroyImageView( + renderer->logicalDevice, + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->view, + NULL + ); + + SDL_free(swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture); + SDL_free(swapchainData->textureContainers[i].activeTextureHandle); + } + + SDL_free(swapchainData->textureContainers); + + renderer->vkDestroySwapchainKHR( + renderer->logicalDevice, + swapchainData->swapchain, + NULL + ); + + renderer->vkDestroySurfaceKHR( + renderer->instance, + swapchainData->surface, + NULL + ); + + for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) + { + renderer->vkDestroySemaphore( + renderer->logicalDevice, + swapchainData->imageAvailableSemaphore[i], + NULL + ); + + renderer->vkDestroySemaphore( + renderer->logicalDevice, + swapchainData->renderFinishedSemaphore[i], + NULL + ); + } + + windowData->swapchainData = NULL; + SDL_free(swapchainData); +} + +/* Descriptor pool stuff */ + +static SDL_bool VULKAN_INTERNAL_CreateDescriptorPool( + VulkanRenderer *renderer, + VulkanDescriptorInfo *descriptorInfos, + Uint32 descriptorInfoCount, + Uint32 descriptorSetPoolSize, + VkDescriptorPool *pDescriptorPool +) { + VkDescriptorPoolSize *descriptorPoolSizes; + VkDescriptorPoolCreateInfo descriptorPoolInfo; + VkResult vulkanResult; + Uint32 i; + + descriptorPoolSizes = NULL; + + if (descriptorInfoCount > 0) + { + descriptorPoolSizes = SDL_stack_alloc(VkDescriptorPoolSize, descriptorInfoCount); + + for (i = 0; i < descriptorInfoCount; i += 1) + { + descriptorPoolSizes[i].type = descriptorInfos[i].descriptorType; + descriptorPoolSizes[i].descriptorCount = descriptorSetPoolSize; + } + } + + descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptorPoolInfo.pNext = NULL; + descriptorPoolInfo.flags = 0; + descriptorPoolInfo.maxSets = descriptorSetPoolSize; + descriptorPoolInfo.poolSizeCount = descriptorInfoCount; + descriptorPoolInfo.pPoolSizes = descriptorPoolSizes; + + vulkanResult = renderer->vkCreateDescriptorPool( + renderer->logicalDevice, + &descriptorPoolInfo, + NULL, + pDescriptorPool + ); + + SDL_stack_free(descriptorPoolSizes); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreateDescriptorPool", vulkanResult); + return SDL_FALSE; + } + + return SDL_TRUE; +} + +static SDL_bool VULKAN_INTERNAL_AllocateDescriptorSets( + VulkanRenderer *renderer, + VkDescriptorPool descriptorPool, + VkDescriptorSetLayout descriptorSetLayout, + Uint32 descriptorSetCount, + VkDescriptorSet *descriptorSetArray +) { + VkDescriptorSetAllocateInfo descriptorSetAllocateInfo; + VkDescriptorSetLayout *descriptorSetLayouts = SDL_stack_alloc(VkDescriptorSetLayout, descriptorSetCount); + VkResult vulkanResult; + Uint32 i; + + for (i = 0; i < descriptorSetCount; i += 1) + { + descriptorSetLayouts[i] = descriptorSetLayout; + } + + descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + descriptorSetAllocateInfo.pNext = NULL; + descriptorSetAllocateInfo.descriptorPool = descriptorPool; + descriptorSetAllocateInfo.descriptorSetCount = descriptorSetCount; + descriptorSetAllocateInfo.pSetLayouts = descriptorSetLayouts; + + vulkanResult = renderer->vkAllocateDescriptorSets( + renderer->logicalDevice, + &descriptorSetAllocateInfo, + descriptorSetArray + ); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkAllocateDescriptorSets", vulkanResult); + SDL_stack_free(descriptorSetLayouts); + return SDL_FALSE; + } + + SDL_stack_free(descriptorSetLayouts); + return SDL_TRUE; +} + +static void VULKAN_INTERNAL_InitializeDescriptorSetPool( + VulkanRenderer *renderer, + DescriptorSetPool *descriptorSetPool +) { + descriptorSetPool->lock = SDL_CreateMutex(); + + /* Descriptor set layout and descriptor infos are already set when this function is called */ + + descriptorSetPool->descriptorPoolCount = 1; + descriptorSetPool->descriptorPools = SDL_malloc(sizeof(VkDescriptorPool)); + descriptorSetPool->nextPoolSize = DESCRIPTOR_POOL_STARTING_SIZE * 2; + + VULKAN_INTERNAL_CreateDescriptorPool( + renderer, + descriptorSetPool->descriptorInfos, + descriptorSetPool->descriptorInfoCount, + DESCRIPTOR_POOL_STARTING_SIZE, + &descriptorSetPool->descriptorPools[0] + ); + + descriptorSetPool->inactiveDescriptorSetCapacity = DESCRIPTOR_POOL_STARTING_SIZE; + descriptorSetPool->inactiveDescriptorSetCount = DESCRIPTOR_POOL_STARTING_SIZE; + descriptorSetPool->inactiveDescriptorSets = SDL_malloc( + sizeof(VkDescriptorSet) * DESCRIPTOR_POOL_STARTING_SIZE + ); + + VULKAN_INTERNAL_AllocateDescriptorSets( + renderer, + descriptorSetPool->descriptorPools[0], + descriptorSetPool->descriptorSetLayout, + DESCRIPTOR_POOL_STARTING_SIZE, + descriptorSetPool->inactiveDescriptorSets + ); +} + +static SDL_bool VULKAN_INTERNAL_InitializeGraphicsPipelineResourceLayout( + VulkanRenderer *renderer, + Refresh_GraphicsPipelineResourceInfo *vertexResourceInfo, + Refresh_GraphicsPipelineResourceInfo *fragmentResourceInfo, + VulkanGraphicsPipelineResourceLayout *pipelineResourceLayout +) { + VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[MAX_TEXTURE_SAMPLERS_PER_STAGE + MAX_STORAGE_TEXTURES_PER_STAGE + MAX_STORAGE_BUFFERS_PER_STAGE]; + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo; + VkDescriptorSetLayout descriptorSetLayouts[4]; + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo; + DescriptorSetPool *descriptorSetPool; + VkResult vulkanResult; + Uint32 i; + + pipelineResourceLayout->vertexSamplerCount = vertexResourceInfo->samplerCount; + pipelineResourceLayout->vertexStorageTextureCount = vertexResourceInfo->storageTextureCount; + pipelineResourceLayout->vertexStorageBufferCount = vertexResourceInfo->storageBufferCount; + pipelineResourceLayout->vertexUniformBufferCount = vertexResourceInfo->uniformBufferCount; + + pipelineResourceLayout->fragmentSamplerCount = fragmentResourceInfo->samplerCount; + pipelineResourceLayout->fragmentStorageTextureCount = fragmentResourceInfo->storageTextureCount; + pipelineResourceLayout->fragmentStorageBufferCount = fragmentResourceInfo->storageBufferCount; + pipelineResourceLayout->fragmentUniformBufferCount = fragmentResourceInfo->uniformBufferCount; + + /* Vertex Resources */ + + descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetLayoutCreateInfo.pNext = NULL; + descriptorSetLayoutCreateInfo.flags = 0; + descriptorSetLayoutCreateInfo.pBindings = NULL; + descriptorSetLayoutCreateInfo.bindingCount = + vertexResourceInfo->samplerCount + + vertexResourceInfo->storageTextureCount + + vertexResourceInfo->storageBufferCount; + + descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[0]; + + descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount; + descriptorSetPool->descriptorInfos = NULL; + + if (descriptorSetLayoutCreateInfo.bindingCount > 0) + { + descriptorSetPool->descriptorInfos = SDL_malloc( + descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo) + ); + + for (i = 0; i < vertexResourceInfo->samplerCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_VERTEX_BIT; + } + + for (i = vertexResourceInfo->samplerCount; i < vertexResourceInfo->samplerCount + vertexResourceInfo->storageTextureCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_VERTEX_BIT; + } + + for (i = vertexResourceInfo->samplerCount + vertexResourceInfo->storageTextureCount; i < descriptorSetLayoutCreateInfo.bindingCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_VERTEX_BIT; + } + + descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings; + } + + vulkanResult = renderer->vkCreateDescriptorSetLayout( + renderer->logicalDevice, + &descriptorSetLayoutCreateInfo, + NULL, + &descriptorSetPool->descriptorSetLayout + ); + + descriptorSetLayouts[0] = descriptorSetPool->descriptorSetLayout; + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult); + return SDL_FALSE; + } + + /* Vertex UBOs */ + + descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[1]; + + descriptorSetLayoutCreateInfo.bindingCount = pipelineResourceLayout->vertexUniformBufferCount; + descriptorSetLayoutCreateInfo.pBindings = NULL; + + descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount; + descriptorSetPool->descriptorInfos = NULL; + + if (descriptorSetLayoutCreateInfo.bindingCount > 0) + { + descriptorSetPool->descriptorInfos = SDL_malloc( + descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo) + ); + + for (i = 0; i < vertexResourceInfo->uniformBufferCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_VERTEX_BIT; + } + + descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings; + } + + vulkanResult = renderer->vkCreateDescriptorSetLayout( + renderer->logicalDevice, + &descriptorSetLayoutCreateInfo, + NULL, + &descriptorSetPool->descriptorSetLayout + ); + + descriptorSetLayouts[1] = descriptorSetPool->descriptorSetLayout; + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult); + return SDL_FALSE; + } + + /* Fragment resources */ + + descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[2]; + + descriptorSetLayoutCreateInfo.bindingCount = + fragmentResourceInfo->samplerCount + + fragmentResourceInfo->storageTextureCount + + fragmentResourceInfo->storageBufferCount; + + descriptorSetLayoutCreateInfo.pBindings = NULL; + + descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount; + descriptorSetPool->descriptorInfos = NULL; + + if (descriptorSetLayoutCreateInfo.bindingCount > 0) + { + descriptorSetPool->descriptorInfos = SDL_malloc( + descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo) + ); + + for (i = 0; i < fragmentResourceInfo->samplerCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_FRAGMENT_BIT; + } + + for (i = fragmentResourceInfo->samplerCount; i < fragmentResourceInfo->samplerCount + fragmentResourceInfo->storageTextureCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_FRAGMENT_BIT; + } + + for (i = fragmentResourceInfo->samplerCount + fragmentResourceInfo->storageTextureCount; i < descriptorSetLayoutCreateInfo.bindingCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_FRAGMENT_BIT; + } + + descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings; + } + + vulkanResult = renderer->vkCreateDescriptorSetLayout( + renderer->logicalDevice, + &descriptorSetLayoutCreateInfo, + NULL, + &descriptorSetPool->descriptorSetLayout + ); + + descriptorSetLayouts[2] = descriptorSetPool->descriptorSetLayout; + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult); + return SDL_FALSE; + } + + /* Fragment UBOs */ + + descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[3]; + + descriptorSetLayoutCreateInfo.bindingCount = + pipelineResourceLayout->fragmentUniformBufferCount; + + descriptorSetLayoutCreateInfo.pBindings = NULL; + + descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount; + descriptorSetPool->descriptorInfos = NULL; + + if (descriptorSetLayoutCreateInfo.bindingCount > 0) + { + descriptorSetPool->descriptorInfos = SDL_malloc( + descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo) + ); + + for (i = 0; i < fragmentResourceInfo->uniformBufferCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_FRAGMENT_BIT; + } + + descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings; + } + + vulkanResult = renderer->vkCreateDescriptorSetLayout( + renderer->logicalDevice, + &descriptorSetLayoutCreateInfo, + NULL, + &descriptorSetPool->descriptorSetLayout + ); + + descriptorSetLayouts[3] = descriptorSetPool->descriptorSetLayout; + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult); + return SDL_FALSE; + } + + /* Create the pipeline layout */ + + pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCreateInfo.pNext = NULL; + pipelineLayoutCreateInfo.flags = 0; + pipelineLayoutCreateInfo.setLayoutCount = 4; + pipelineLayoutCreateInfo.pSetLayouts = descriptorSetLayouts; + pipelineLayoutCreateInfo.pushConstantRangeCount = 0; + pipelineLayoutCreateInfo.pPushConstantRanges = NULL; + + vulkanResult = renderer->vkCreatePipelineLayout( + renderer->logicalDevice, + &pipelineLayoutCreateInfo, + NULL, + &pipelineResourceLayout->pipelineLayout + ); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreatePipelineLayout", vulkanResult); + return SDL_FALSE; + } + + for (i = 0; i < 4; i += 1) + { + VULKAN_INTERNAL_InitializeDescriptorSetPool( + renderer, + &pipelineResourceLayout->descriptorSetPools[i] + ); + } + + return SDL_TRUE; +} + +static SDL_bool VULKAN_INTERNAL_InitializeComputePipelineResourceLayout( + VulkanRenderer *renderer, + Refresh_ComputePipelineResourceInfo *resourceLayoutInfo, + VulkanComputePipelineResourceLayout *pipelineResourceLayout +) { + VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[MAX_UNIFORM_BUFFERS_PER_STAGE]; + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo; + VkDescriptorSetLayout descriptorSetLayouts[3]; + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo; + DescriptorSetPool *descriptorSetPool; + VkResult vulkanResult; + Uint32 i; + + pipelineResourceLayout->readOnlyStorageTextureCount = resourceLayoutInfo->readOnlyStorageTextureCount; + pipelineResourceLayout->readOnlyStorageBufferCount = resourceLayoutInfo->readOnlyStorageBufferCount; + pipelineResourceLayout->readWriteStorageTextureCount = resourceLayoutInfo->readWriteStorageTextureCount; + pipelineResourceLayout->readWriteStorageBufferCount = resourceLayoutInfo->readWriteStorageBufferCount; + pipelineResourceLayout->uniformBufferCount = resourceLayoutInfo->uniformBufferCount; + + /* Read-only resources */ + + descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetLayoutCreateInfo.pNext = NULL; + descriptorSetLayoutCreateInfo.flags = 0; + descriptorSetLayoutCreateInfo.pBindings = NULL; + descriptorSetLayoutCreateInfo.bindingCount = + resourceLayoutInfo->readOnlyStorageTextureCount + + resourceLayoutInfo->readOnlyStorageBufferCount; + + descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[0]; + + descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount; + descriptorSetPool->descriptorInfos = NULL; + + if (descriptorSetLayoutCreateInfo.bindingCount > 0) + { + descriptorSetPool->descriptorInfos = SDL_malloc( + descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo) + ); + + for (i = 0; i < resourceLayoutInfo->readOnlyStorageTextureCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_COMPUTE_BIT; + } + + for (i = resourceLayoutInfo->readOnlyStorageTextureCount; i < descriptorSetLayoutCreateInfo.bindingCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_COMPUTE_BIT; + } + + descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings; + } + + vulkanResult = renderer->vkCreateDescriptorSetLayout( + renderer->logicalDevice, + &descriptorSetLayoutCreateInfo, + NULL, + &descriptorSetPool->descriptorSetLayout + ); + + descriptorSetLayouts[0] = descriptorSetPool->descriptorSetLayout; + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult); + return SDL_FALSE; + } + + /* Read-write resources */ + + descriptorSetLayoutCreateInfo.bindingCount = + resourceLayoutInfo->readWriteStorageTextureCount + + resourceLayoutInfo->readWriteStorageBufferCount; + + descriptorSetLayoutCreateInfo.pBindings = NULL; + + descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[1]; + + descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount; + descriptorSetPool->descriptorInfos = NULL; + + if (descriptorSetLayoutCreateInfo.bindingCount > 0) + { + descriptorSetPool->descriptorInfos = SDL_malloc( + descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo) + ); + + for (i = 0; i < resourceLayoutInfo->readWriteStorageTextureCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_COMPUTE_BIT; + } + + for (i = resourceLayoutInfo->readWriteStorageTextureCount; i < descriptorSetLayoutCreateInfo.bindingCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_COMPUTE_BIT; + } + + descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings; + } + + vulkanResult = renderer->vkCreateDescriptorSetLayout( + renderer->logicalDevice, + &descriptorSetLayoutCreateInfo, + NULL, + &descriptorSetPool->descriptorSetLayout + ); + + descriptorSetLayouts[1] = descriptorSetPool->descriptorSetLayout; + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult); + return SDL_FALSE; + } + + /* Uniform buffers */ + + descriptorSetPool = &pipelineResourceLayout->descriptorSetPools[2]; + + descriptorSetLayoutCreateInfo.bindingCount = resourceLayoutInfo->uniformBufferCount; + descriptorSetLayoutCreateInfo.pBindings = NULL; + + descriptorSetPool->descriptorInfoCount = descriptorSetLayoutCreateInfo.bindingCount; + descriptorSetPool->descriptorInfos = NULL; + + if (descriptorSetLayoutCreateInfo.bindingCount > 0) + { + descriptorSetPool->descriptorInfos = SDL_malloc( + descriptorSetPool->descriptorInfoCount * sizeof(VulkanDescriptorInfo) + ); + + for (i = 0; i < resourceLayoutInfo->uniformBufferCount; i += 1) + { + descriptorSetLayoutBindings[i].binding = i; + descriptorSetLayoutBindings[i].descriptorCount = 1; + descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + descriptorSetLayoutBindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + descriptorSetLayoutBindings[i].pImmutableSamplers = NULL; + + descriptorSetPool->descriptorInfos[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + descriptorSetPool->descriptorInfos[i].stageFlag = VK_SHADER_STAGE_COMPUTE_BIT; + } + + descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings; + } + + vulkanResult = renderer->vkCreateDescriptorSetLayout( + renderer->logicalDevice, + &descriptorSetLayoutCreateInfo, + NULL, + &descriptorSetPool->descriptorSetLayout + ); + + descriptorSetLayouts[2] = descriptorSetPool->descriptorSetLayout; + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreateDescriptorSetLayout", vulkanResult); + return SDL_FALSE; + } + + /* Create the pipeline layout */ + + pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCreateInfo.pNext = NULL; + pipelineLayoutCreateInfo.flags = 0; + pipelineLayoutCreateInfo.setLayoutCount = 3; + pipelineLayoutCreateInfo.pSetLayouts = descriptorSetLayouts; + pipelineLayoutCreateInfo.pushConstantRangeCount = 0; + pipelineLayoutCreateInfo.pPushConstantRanges = NULL; + + vulkanResult = renderer->vkCreatePipelineLayout( + renderer->logicalDevice, + &pipelineLayoutCreateInfo, + NULL, + &pipelineResourceLayout->pipelineLayout + ); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreatePipelineLayout", vulkanResult); + return SDL_FALSE; + } + + for (i = 0; i < 3; i += 1) + { + VULKAN_INTERNAL_InitializeDescriptorSetPool( + renderer, + &pipelineResourceLayout->descriptorSetPools[i] + ); + } + + return SDL_TRUE; +} + +/* Data Buffer */ + +static VulkanBuffer* VULKAN_INTERNAL_CreateBuffer( + VulkanRenderer *renderer, + VkDeviceSize size, + Refresh_BufferUsageFlags usageFlags, + VulkanBufferType type +) { + VulkanBuffer* buffer; + VkResult vulkanResult; + VkBufferCreateInfo bufferCreateInfo; + VkBufferUsageFlags vulkanUsageFlags = 0; + Uint8 bindResult; + + if (usageFlags & REFRESH_BUFFERUSAGE_VERTEX_BIT) + { + vulkanUsageFlags |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + } + + if (usageFlags & REFRESH_BUFFERUSAGE_INDEX_BIT) + { + vulkanUsageFlags |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + } + + if (usageFlags & ( + REFRESH_BUFFERUSAGE_GRAPHICS_STORAGE_READ_BIT | + REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_READ_BIT | + REFRESH_BUFFERUSAGE_COMPUTE_STORAGE_WRITE_BIT + )) { + vulkanUsageFlags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + } + + if (usageFlags & REFRESH_BUFFERUSAGE_INDIRECT_BIT) + { + vulkanUsageFlags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; + } + + if (type == VULKAN_BUFFER_TYPE_UNIFORM) + { + vulkanUsageFlags |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + } + else + { + /* GPU buffers need transfer bits for defrag, transfer buffers need them for transfers */ + vulkanUsageFlags |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + } + + buffer = SDL_malloc(sizeof(VulkanBuffer)); + + buffer->size = size; + buffer->usageFlags = usageFlags; + buffer->type = type; + buffer->markedForDestroy = 0; + buffer->transitioned = SDL_FALSE; + + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.pNext = NULL; + bufferCreateInfo.flags = 0; + bufferCreateInfo.size = size; + bufferCreateInfo.usage = vulkanUsageFlags; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + bufferCreateInfo.queueFamilyIndexCount = 1; + bufferCreateInfo.pQueueFamilyIndices = &renderer->queueFamilyIndex; + + /* Set transfer bits so we can defrag */ + bufferCreateInfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + vulkanResult = renderer->vkCreateBuffer( + renderer->logicalDevice, + &bufferCreateInfo, + NULL, + &buffer->buffer + ); + VULKAN_ERROR_CHECK(vulkanResult, vkCreateBuffer, 0) + + bindResult = VULKAN_INTERNAL_BindMemoryForBuffer( + renderer, + buffer->buffer, + buffer->size, + buffer->type, + &buffer->usedRegion + ); + + if (bindResult != 1) + { + renderer->vkDestroyBuffer( + renderer->logicalDevice, + buffer->buffer, + NULL); + + return NULL; + } + + buffer->usedRegion->vulkanBuffer = buffer; /* lol */ + buffer->handle = NULL; + + SDL_AtomicSet(&buffer->referenceCount, 0); + + return buffer; +} + +/* Indirection so we can cleanly defrag buffers */ +static VulkanBufferHandle* VULKAN_INTERNAL_CreateBufferHandle( + VulkanRenderer *renderer, + Uint32 sizeInBytes, + Refresh_BufferUsageFlags usageFlags, + VulkanBufferType type +) { + VulkanBufferHandle* bufferHandle; + VulkanBuffer* buffer; + + buffer = VULKAN_INTERNAL_CreateBuffer( + renderer, + sizeInBytes, + usageFlags, + type + ); + + if (buffer == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create buffer!"); + return NULL; + } + + bufferHandle = SDL_malloc(sizeof(VulkanBufferHandle)); + bufferHandle->vulkanBuffer = buffer; + bufferHandle->container = NULL; + + buffer->handle = bufferHandle; + + return bufferHandle; +} + +static VulkanBufferContainer* VULKAN_INTERNAL_CreateBufferContainer( + VulkanRenderer *renderer, + Uint32 sizeInBytes, + Refresh_BufferUsageFlags usageFlags, + VulkanBufferType type +) { + VulkanBufferContainer *bufferContainer; + VulkanBufferHandle *bufferHandle; + + bufferHandle = VULKAN_INTERNAL_CreateBufferHandle( + renderer, + sizeInBytes, + usageFlags, + type + ); + + if (bufferHandle == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create buffer container!"); + return NULL; + } + + bufferContainer = SDL_malloc(sizeof(VulkanBufferContainer)); + + bufferContainer->activeBufferHandle = bufferHandle; + bufferHandle->container = bufferContainer; + + bufferContainer->bufferCapacity = 1; + bufferContainer->bufferCount = 1; + bufferContainer->bufferHandles = SDL_malloc( + bufferContainer->bufferCapacity * sizeof(VulkanBufferHandle*) + ); + bufferContainer->bufferHandles[0] = bufferContainer->activeBufferHandle; + bufferContainer->debugName = NULL; + + return bufferContainer; +} + +/* Texture Slice Utilities */ + +static Uint32 VULKAN_INTERNAL_GetTextureSliceIndex( + VulkanTexture *texture, + Uint32 layer, + Uint32 level +) { + return (layer * texture->levelCount) + level; +} + +static VulkanTextureSlice* VULKAN_INTERNAL_FetchTextureSlice( + VulkanTexture *texture, + Uint32 layer, + Uint32 level +) { + return &texture->slices[ + VULKAN_INTERNAL_GetTextureSliceIndex( + texture, + layer, + level + ) + ]; +} + +static VulkanTextureSlice* VULKAN_INTERNAL_SDLToVulkanTextureSlice( + Refresh_TextureSlice *textureSlice +) { + return VULKAN_INTERNAL_FetchTextureSlice( + ((VulkanTextureContainer*) textureSlice->texture)->activeTextureHandle->vulkanTexture, + textureSlice->layer, + textureSlice->mipLevel + ); +} + +static void VULKAN_INTERNAL_CreateSliceView( + VulkanRenderer *renderer, + VulkanTexture *texture, + Uint32 layer, + Uint32 level, + VkComponentMapping swizzle, + VkImageView *pView +) { + VkResult vulkanResult; + VkImageViewCreateInfo imageViewCreateInfo; + + /* create framebuffer compatible views for RenderTarget */ + imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewCreateInfo.pNext = NULL; + imageViewCreateInfo.flags = 0; + imageViewCreateInfo.image = texture->image; + imageViewCreateInfo.format = texture->format; + imageViewCreateInfo.components = swizzle; + imageViewCreateInfo.subresourceRange.aspectMask = texture->aspectFlags; + imageViewCreateInfo.subresourceRange.baseMipLevel = level; + imageViewCreateInfo.subresourceRange.levelCount = 1; + imageViewCreateInfo.subresourceRange.baseArrayLayer = layer; + imageViewCreateInfo.subresourceRange.layerCount = 1; + imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + + vulkanResult = renderer->vkCreateImageView( + renderer->logicalDevice, + &imageViewCreateInfo, + NULL, + pView + ); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError( + "vkCreateImageView", + vulkanResult + ); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create color attachment image view"); + *pView = (VkImageView) VK_NULL_HANDLE; + return; + } +} + +/* Swapchain */ + +static Uint8 VULKAN_INTERNAL_QuerySwapchainSupport( + VulkanRenderer *renderer, + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + SwapchainSupportDetails *outputDetails +) { + VkResult result; + VkBool32 supportsPresent; + + renderer->vkGetPhysicalDeviceSurfaceSupportKHR( + physicalDevice, + renderer->queueFamilyIndex, + surface, + &supportsPresent + ); + + if (!supportsPresent) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "This surface does not support presenting!"); + return 0; + } + + /* Initialize these in case anything fails */ + outputDetails->formatsLength = 0; + outputDetails->presentModesLength = 0; + + /* Run the device surface queries */ + result = renderer->vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + physicalDevice, + surface, + &outputDetails->capabilities + ); + VULKAN_ERROR_CHECK(result, vkGetPhysicalDeviceSurfaceCapabilitiesKHR, 0) + + if (!(outputDetails->capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Opaque presentation unsupported! Expect weird transparency bugs!"); + } + + result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR( + physicalDevice, + surface, + &outputDetails->formatsLength, + NULL + ); + VULKAN_ERROR_CHECK(result, vkGetPhysicalDeviceSurfaceFormatsKHR, 0) + result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR( + physicalDevice, + surface, + &outputDetails->presentModesLength, + NULL + ); + VULKAN_ERROR_CHECK(result, vkGetPhysicalDeviceSurfacePresentModesKHR, 0) + + /* Generate the arrays, if applicable */ + + outputDetails->formats = NULL; + if (outputDetails->formatsLength != 0) + { + outputDetails->formats = (VkSurfaceFormatKHR*) SDL_malloc( + sizeof(VkSurfaceFormatKHR) * outputDetails->formatsLength + ); + + if (!outputDetails->formats) + { + SDL_OutOfMemory(); + return 0; + } + + result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR( + physicalDevice, + surface, + &outputDetails->formatsLength, + outputDetails->formats + ); + if (result != VK_SUCCESS) + { + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "vkGetPhysicalDeviceSurfaceFormatsKHR: %s", + VkErrorMessages(result) + ); + + SDL_free(outputDetails->formats); + return 0; + } + } + + outputDetails->presentModes = NULL; + if (outputDetails->presentModesLength != 0) + { + outputDetails->presentModes = (VkPresentModeKHR*) SDL_malloc( + sizeof(VkPresentModeKHR) * outputDetails->presentModesLength + ); + + if (!outputDetails->presentModes) + { + SDL_OutOfMemory(); + return 0; + } + + result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR( + physicalDevice, + surface, + &outputDetails->presentModesLength, + outputDetails->presentModes + ); + if (result != VK_SUCCESS) + { + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "vkGetPhysicalDeviceSurfacePresentModesKHR: %s", + VkErrorMessages(result) + ); + + SDL_free(outputDetails->formats); + SDL_free(outputDetails->presentModes); + return 0; + } + } + + /* If we made it here, all the queries were successful. This does NOT + * necessarily mean there are any supported formats or present modes! + */ + return 1; +} + +static SDL_bool VULKAN_INTERNAL_VerifySwapSurfaceFormat( + VkFormat desiredFormat, + VkColorSpaceKHR desiredColorSpace, + VkSurfaceFormatKHR *availableFormats, + Uint32 availableFormatsLength +) { + Uint32 i; + for (i = 0; i < availableFormatsLength; i += 1) + { + if ( availableFormats[i].format == desiredFormat && + availableFormats[i].colorSpace == desiredColorSpace ) + { + return SDL_TRUE; + } + } + + return SDL_FALSE; +} + +static SDL_bool VULKAN_INTERNAL_VerifySwapPresentMode( + VkPresentModeKHR presentMode, + VkPresentModeKHR *availablePresentModes, + Uint32 availablePresentModesLength +) { + Uint32 i; + + for (i = 0; i < availablePresentModesLength; i += 1) + { + if (availablePresentModes[i] == presentMode) + { + return SDL_TRUE; + } + } + + return SDL_FALSE; +} + +static Uint8 VULKAN_INTERNAL_CreateSwapchain( + VulkanRenderer *renderer, + WindowData *windowData +) { + VkResult vulkanResult; + VulkanSwapchainData *swapchainData; + VkSwapchainCreateInfoKHR swapchainCreateInfo; + VkImage *swapchainImages; + VkImageViewCreateInfo imageViewCreateInfo; + VkSemaphoreCreateInfo semaphoreCreateInfo; + SwapchainSupportDetails swapchainSupportDetails; + Sint32 drawableWidth, drawableHeight; + Uint32 i; + + swapchainData = SDL_malloc(sizeof(VulkanSwapchainData)); + swapchainData->frameCounter = 0; + + /* Each swapchain must have its own surface. */ + + if (!SDL_Vulkan_CreateSurface( + windowData->window, + renderer->instance, + &swapchainData->surface + )) { + SDL_free(swapchainData); + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "Vulkan_CreateSurface failed: %s", + SDL_GetError() + ); + return 0; + } + + if (!VULKAN_INTERNAL_QuerySwapchainSupport( + renderer, + renderer->physicalDevice, + swapchainData->surface, + &swapchainSupportDetails + )) { + renderer->vkDestroySurfaceKHR( + renderer->instance, + swapchainData->surface, + NULL + ); + if (swapchainSupportDetails.formatsLength > 0) + { + SDL_free(swapchainSupportDetails.formats); + } + if (swapchainSupportDetails.presentModesLength > 0) + { + SDL_free(swapchainSupportDetails.presentModes); + } + SDL_free(swapchainData); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device does not support swap chain creation"); + return 0; + } + + if ( swapchainSupportDetails.capabilities.currentExtent.width == 0 || + swapchainSupportDetails.capabilities.currentExtent.height == 0) + { + /* Not an error, just minimize behavior! */ + renderer->vkDestroySurfaceKHR( + renderer->instance, + swapchainData->surface, + NULL + ); + if (swapchainSupportDetails.formatsLength > 0) + { + SDL_free(swapchainSupportDetails.formats); + } + if (swapchainSupportDetails.presentModesLength > 0) + { + SDL_free(swapchainSupportDetails.presentModes); + } + SDL_free(swapchainData); + return 0; + } + + swapchainData->format = SwapchainCompositionToFormat[windowData->swapchainComposition]; + swapchainData->colorSpace = SwapchainCompositionToColorSpace[windowData->swapchainComposition]; + swapchainData->swapchainSwizzle.r = VK_COMPONENT_SWIZZLE_IDENTITY; + swapchainData->swapchainSwizzle.g = VK_COMPONENT_SWIZZLE_IDENTITY; + swapchainData->swapchainSwizzle.b = VK_COMPONENT_SWIZZLE_IDENTITY; + swapchainData->swapchainSwizzle.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + if (!VULKAN_INTERNAL_VerifySwapSurfaceFormat( + swapchainData->format, + swapchainData->colorSpace, + swapchainSupportDetails.formats, + swapchainSupportDetails.formatsLength + )) { + if (!BGRToRGBSwapchainFormat( + swapchainData->format, + &swapchainData->format + )) { + renderer->vkDestroySurfaceKHR( + renderer->instance, + swapchainData->surface, + NULL + ); + + if (swapchainSupportDetails.formatsLength > 0) + { + SDL_free(swapchainSupportDetails.formats); + } + + if (swapchainSupportDetails.presentModesLength > 0) + { + SDL_free(swapchainSupportDetails.presentModes); + } + + SDL_free(swapchainData); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device does not support requested colorspace!"); + return 0; + } + + if (!VULKAN_INTERNAL_VerifySwapSurfaceFormat( + swapchainData->format, + swapchainData->colorSpace, + swapchainSupportDetails.formats, + swapchainSupportDetails.formatsLength + )) { + renderer->vkDestroySurfaceKHR( + renderer->instance, + swapchainData->surface, + NULL + ); + + if (swapchainSupportDetails.formatsLength > 0) + { + SDL_free(swapchainSupportDetails.formats); + } + + if (swapchainSupportDetails.presentModesLength > 0) + { + SDL_free(swapchainSupportDetails.presentModes); + } + + SDL_free(swapchainData); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device does not support requested colorspace!"); + return 0; + } + } + + if (!VULKAN_INTERNAL_VerifySwapPresentMode( + SDLToVK_PresentMode[windowData->presentMode], + swapchainSupportDetails.presentModes, + swapchainSupportDetails.presentModesLength + )) { + renderer->vkDestroySurfaceKHR( + renderer->instance, + swapchainData->surface, + NULL + ); + + if (swapchainSupportDetails.formatsLength > 0) + { + SDL_free(swapchainSupportDetails.formats); + } + + if (swapchainSupportDetails.presentModesLength > 0) + { + SDL_free(swapchainSupportDetails.presentModes); + } + + SDL_free(swapchainData); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device does not support requested present mode!"); + return 0; + } + + swapchainData->presentMode = SDLToVK_PresentMode[windowData->presentMode]; + + /* Should sync now to be sure that our swapchain size is correct */ + /* TODO: how to backport SyncWindow? */ + + SDL_GetWindowSizeInPixels( + windowData->window, + &drawableWidth, + &drawableHeight + ); + + if ( drawableWidth < swapchainSupportDetails.capabilities.minImageExtent.width || + drawableWidth > swapchainSupportDetails.capabilities.maxImageExtent.width || + drawableHeight < swapchainSupportDetails.capabilities.minImageExtent.height || + drawableHeight > swapchainSupportDetails.capabilities.maxImageExtent.height ) + { + if (swapchainSupportDetails.capabilities.currentExtent.width != UINT32_MAX) + { + drawableWidth = VULKAN_INTERNAL_clamp( + drawableWidth, + swapchainSupportDetails.capabilities.minImageExtent.width, + swapchainSupportDetails.capabilities.maxImageExtent.width + ); + drawableHeight = VULKAN_INTERNAL_clamp( + drawableHeight, + swapchainSupportDetails.capabilities.minImageExtent.height, + swapchainSupportDetails.capabilities.maxImageExtent.height + ); + } + else + { + renderer->vkDestroySurfaceKHR( + renderer->instance, + swapchainData->surface, + NULL + ); + if (swapchainSupportDetails.formatsLength > 0) + { + SDL_free(swapchainSupportDetails.formats); + } + if (swapchainSupportDetails.presentModesLength > 0) + { + SDL_free(swapchainSupportDetails.presentModes); + } + SDL_free(swapchainData); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No fallback swapchain size available!"); + return 0; + } + } + + swapchainData->extent.width = drawableWidth; + swapchainData->extent.height = drawableHeight; + + swapchainData->imageCount = swapchainSupportDetails.capabilities.minImageCount + 1; + + if ( swapchainSupportDetails.capabilities.maxImageCount > 0 && + swapchainData->imageCount > swapchainSupportDetails.capabilities.maxImageCount ) + { + swapchainData->imageCount = swapchainSupportDetails.capabilities.maxImageCount; + } + + if (swapchainData->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) + { + /* Required for proper triple-buffering. + * Note that this is below the above maxImageCount check! + * If the driver advertises MAILBOX but does not support 3 swap + * images, it's not real mailbox support, so let it fail hard. + * -flibit + */ + swapchainData->imageCount = SDL_max(swapchainData->imageCount, 3); + } + + swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainCreateInfo.pNext = NULL; + swapchainCreateInfo.flags = 0; + swapchainCreateInfo.surface = swapchainData->surface; + swapchainCreateInfo.minImageCount = swapchainData->imageCount; + swapchainCreateInfo.imageFormat = swapchainData->format; + swapchainCreateInfo.imageColorSpace = swapchainData->colorSpace; + swapchainCreateInfo.imageExtent = swapchainData->extent; + swapchainCreateInfo.imageArrayLayers = 1; + swapchainCreateInfo.imageUsage = + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; + swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchainCreateInfo.queueFamilyIndexCount = 0; + swapchainCreateInfo.pQueueFamilyIndices = NULL; + swapchainCreateInfo.preTransform = swapchainSupportDetails.capabilities.currentTransform; + swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchainCreateInfo.presentMode = swapchainData->presentMode; + swapchainCreateInfo.clipped = VK_TRUE; + swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE; + + vulkanResult = renderer->vkCreateSwapchainKHR( + renderer->logicalDevice, + &swapchainCreateInfo, + NULL, + &swapchainData->swapchain + ); + + if (swapchainSupportDetails.formatsLength > 0) + { + SDL_free(swapchainSupportDetails.formats); + } + if (swapchainSupportDetails.presentModesLength > 0) + { + SDL_free(swapchainSupportDetails.presentModes); + } + + if (vulkanResult != VK_SUCCESS) + { + renderer->vkDestroySurfaceKHR( + renderer->instance, + swapchainData->surface, + NULL + ); + SDL_free(swapchainData); + LogVulkanResultAsError("vkCreateSwapchainKHR", vulkanResult); + return 0; + } + + renderer->vkGetSwapchainImagesKHR( + renderer->logicalDevice, + swapchainData->swapchain, + &swapchainData->imageCount, + NULL + ); + + swapchainData->textureContainers = SDL_malloc( + sizeof(VulkanTextureContainer) * swapchainData->imageCount + ); + + if (!swapchainData->textureContainers) + { + SDL_OutOfMemory(); + renderer->vkDestroySurfaceKHR( + renderer->instance, + swapchainData->surface, + NULL + ); + SDL_free(swapchainData); + return 0; + } + + swapchainImages = SDL_stack_alloc(VkImage, swapchainData->imageCount); + + renderer->vkGetSwapchainImagesKHR( + renderer->logicalDevice, + swapchainData->swapchain, + &swapchainData->imageCount, + swapchainImages + ); + + imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewCreateInfo.pNext = NULL; + imageViewCreateInfo.flags = 0; + imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewCreateInfo.format = swapchainData->format; + imageViewCreateInfo.components = swapchainData->swapchainSwizzle; + imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageViewCreateInfo.subresourceRange.baseMipLevel = 0; + imageViewCreateInfo.subresourceRange.levelCount = 1; + imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; + imageViewCreateInfo.subresourceRange.layerCount = 1; + + for (i = 0; i < swapchainData->imageCount; i += 1) + { + swapchainData->textureContainers[i].canBeCycled = 0; + swapchainData->textureContainers[i].textureCapacity = 0; + swapchainData->textureContainers[i].textureCount = 0; + swapchainData->textureContainers[i].textureHandles = NULL; + swapchainData->textureContainers[i].debugName = NULL; + swapchainData->textureContainers[i].activeTextureHandle = SDL_malloc(sizeof(VulkanTextureHandle)); + + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture = SDL_malloc(sizeof(VulkanTexture)); + + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->image = swapchainImages[i]; + + imageViewCreateInfo.image = swapchainImages[i]; + + vulkanResult = renderer->vkCreateImageView( + renderer->logicalDevice, + &imageViewCreateInfo, + NULL, + &swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->view + ); + + if (vulkanResult != VK_SUCCESS) + { + renderer->vkDestroySurfaceKHR( + renderer->instance, + swapchainData->surface, + NULL + ); + SDL_stack_free(swapchainImages); + SDL_free(swapchainData->textureContainers); + SDL_free(swapchainData); + LogVulkanResultAsError("vkCreateImageView", vulkanResult); + return 0; + } + + /* Swapchain memory is managed by the driver */ + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->usedRegion = NULL; + + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->dimensions = swapchainData->extent; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->format = swapchainData->format; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->swizzle = swapchainData->swapchainSwizzle; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->is3D = 0; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->isCube = 0; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->isRenderTarget = 1; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->depth = 1; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->layerCount = 1; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->levelCount = 1; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->sampleCount = VK_SAMPLE_COUNT_1_BIT; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->usageFlags = + REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT; + + swapchainData->textureContainers[i].activeTextureHandle->container = NULL; + + /* Create slice */ + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->sliceCount = 1; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->slices = SDL_malloc(sizeof(VulkanTextureSlice)); + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->slices[0].parent = swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->slices[0].layer = 0; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->slices[0].level = 0; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->slices[0].transitioned = SDL_TRUE; + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->slices[0].msaaTexHandle = NULL; + + VULKAN_INTERNAL_CreateSliceView( + renderer, + swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture, + 0, + 0, + swapchainData->swapchainSwizzle, + &swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->slices[0].view + ); + SDL_AtomicSet(&swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->slices[0].referenceCount, 0); + } + + SDL_stack_free(swapchainImages); + + semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreCreateInfo.pNext = NULL; + semaphoreCreateInfo.flags = 0; + + for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) + { + renderer->vkCreateSemaphore( + renderer->logicalDevice, + &semaphoreCreateInfo, + NULL, + &swapchainData->imageAvailableSemaphore[i] + ); + + renderer->vkCreateSemaphore( + renderer->logicalDevice, + &semaphoreCreateInfo, + NULL, + &swapchainData->renderFinishedSemaphore[i] + ); + + swapchainData->inFlightFences[i] = NULL; + } + + windowData->swapchainData = swapchainData; + return 1; +} + +/* Command Buffers */ + +static void VULKAN_INTERNAL_BeginCommandBuffer( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer +) { + VkCommandBufferBeginInfo beginInfo; + VkResult result; + + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.pNext = NULL; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = NULL; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + result = renderer->vkBeginCommandBuffer( + commandBuffer->commandBuffer, + &beginInfo + ); + + if (result != VK_SUCCESS) + { + LogVulkanResultAsError("vkBeginCommandBuffer", result); + } +} + +static void VULKAN_INTERNAL_EndCommandBuffer( + VulkanRenderer* renderer, + VulkanCommandBuffer *commandBuffer +) { + VkResult result; + + result = renderer->vkEndCommandBuffer( + commandBuffer->commandBuffer + ); + + if (result != VK_SUCCESS) + { + LogVulkanResultAsError("vkEndCommandBuffer", result); + } +} + +static void VULKAN_DestroyDevice( + Refresh_Device *device +) { + VulkanRenderer* renderer = (VulkanRenderer*) device->driverData; + CommandPoolHashArray commandPoolHashArray; + VulkanMemorySubAllocator *allocator; + Sint32 i, j, k; + + VULKAN_Wait(device->driverData); + + for (i = renderer->claimedWindowCount - 1; i >= 0; i -= 1) + { + VULKAN_UnclaimWindow(device->driverData, renderer->claimedWindows[i]->window); + } + + SDL_free(renderer->claimedWindows); + + VULKAN_Wait(device->driverData); + + SDL_free(renderer->submittedCommandBuffers); + + for (i = 0; i < renderer->fencePool.availableFenceCount; i += 1) + { + renderer->vkDestroyFence( + renderer->logicalDevice, + renderer->fencePool.availableFences[i]->fence, + NULL + ); + + SDL_free(renderer->fencePool.availableFences[i]); + } + + SDL_free(renderer->fencePool.availableFences); + SDL_DestroyMutex(renderer->fencePool.lock); + + for (i = 0; i < NUM_COMMAND_POOL_BUCKETS; i += 1) + { + commandPoolHashArray = renderer->commandPoolHashTable.buckets[i]; + for (j = 0; j < commandPoolHashArray.count; j += 1) + { + VULKAN_INTERNAL_DestroyCommandPool( + renderer, + commandPoolHashArray.elements[j].value + ); + } + + if (commandPoolHashArray.elements != NULL) + { + SDL_free(commandPoolHashArray.elements); + } + } + + renderer->vkDestroyQueryPool( + renderer->logicalDevice, + renderer->queryPool, + NULL + ); + + for (i = 0; i < renderer->framebufferHashArray.count; i += 1) + { + VULKAN_INTERNAL_DestroyFramebuffer( + renderer, + renderer->framebufferHashArray.elements[i].value + ); + } + + SDL_free(renderer->framebufferHashArray.elements); + + for (i = 0; i < renderer->renderPassHashArray.count; i += 1) + { + renderer->vkDestroyRenderPass( + renderer->logicalDevice, + renderer->renderPassHashArray.elements[i].value, + NULL + ); + } + + SDL_free(renderer->renderPassHashArray.elements); + + for (i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) + { + allocator = &renderer->memoryAllocator->subAllocators[i]; + + for (j = allocator->allocationCount - 1; j >= 0; j -= 1) + { + for (k = allocator->allocations[j]->usedRegionCount - 1; k >= 0; k -= 1) + { + VULKAN_INTERNAL_RemoveMemoryUsedRegion( + renderer, + allocator->allocations[j]->usedRegions[k] + ); + } + + VULKAN_INTERNAL_DeallocateMemory( + renderer, + allocator, + j + ); + } + + if (renderer->memoryAllocator->subAllocators[i].allocations != NULL) + { + SDL_free(renderer->memoryAllocator->subAllocators[i].allocations); + } + + SDL_free(renderer->memoryAllocator->subAllocators[i].sortedFreeRegions); + } + + SDL_free(renderer->memoryAllocator); + + SDL_free(renderer->texturesToDestroy); + SDL_free(renderer->buffersToDestroy); + SDL_free(renderer->graphicsPipelinesToDestroy); + SDL_free(renderer->computePipelinesToDestroy); + SDL_free(renderer->shadersToDestroy); + SDL_free(renderer->samplersToDestroy); + SDL_free(renderer->framebuffersToDestroy); + + SDL_DestroyMutex(renderer->allocatorLock); + SDL_DestroyMutex(renderer->disposeLock); + SDL_DestroyMutex(renderer->submitLock); + SDL_DestroyMutex(renderer->acquireCommandBufferLock); + SDL_DestroyMutex(renderer->renderPassFetchLock); + SDL_DestroyMutex(renderer->framebufferFetchLock); + SDL_DestroyMutex(renderer->queryLock); + + renderer->vkDestroyDevice(renderer->logicalDevice, NULL); + renderer->vkDestroyInstance(renderer->instance, NULL); + + SDL_free(renderer); + SDL_free(device); + SDL_Vulkan_UnloadLibrary(); +} + +static VkDescriptorSet VULKAN_INTERNAL_FetchDescriptorSet( + VulkanRenderer *renderer, + VulkanCommandBuffer *vulkanCommandBuffer, + DescriptorSetPool *descriptorSetPool +) { + VkDescriptorSet descriptorSet; + + SDL_LockMutex(descriptorSetPool->lock); + + /* If no inactive descriptor sets remain, create a new pool and allocate new inactive sets */ + + if (descriptorSetPool->inactiveDescriptorSetCount == 0) + { + descriptorSetPool->descriptorPoolCount += 1; + descriptorSetPool->descriptorPools = SDL_realloc( + descriptorSetPool->descriptorPools, + sizeof(VkDescriptorPool) * descriptorSetPool->descriptorPoolCount + ); + + if (!VULKAN_INTERNAL_CreateDescriptorPool( + renderer, + descriptorSetPool->descriptorInfos, + descriptorSetPool->descriptorInfoCount, + descriptorSetPool->nextPoolSize, + &descriptorSetPool->descriptorPools[descriptorSetPool->descriptorPoolCount - 1] + )) { + SDL_UnlockMutex(descriptorSetPool->lock); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create descriptor pool!"); + return VK_NULL_HANDLE; + } + + descriptorSetPool->inactiveDescriptorSetCapacity += descriptorSetPool->nextPoolSize; + + descriptorSetPool->inactiveDescriptorSets = SDL_realloc( + descriptorSetPool->inactiveDescriptorSets, + sizeof(VkDescriptorSet) * descriptorSetPool->inactiveDescriptorSetCapacity + ); + + if (!VULKAN_INTERNAL_AllocateDescriptorSets( + renderer, + descriptorSetPool->descriptorPools[descriptorSetPool->descriptorPoolCount - 1], + descriptorSetPool->descriptorSetLayout, + descriptorSetPool->nextPoolSize, + descriptorSetPool->inactiveDescriptorSets + )) { + SDL_UnlockMutex(descriptorSetPool->lock); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to allocate descriptor sets!"); + return VK_NULL_HANDLE; + } + + descriptorSetPool->inactiveDescriptorSetCount = descriptorSetPool->nextPoolSize; + + descriptorSetPool->nextPoolSize *= 2; + } + + descriptorSet = descriptorSetPool->inactiveDescriptorSets[descriptorSetPool->inactiveDescriptorSetCount - 1]; + descriptorSetPool->inactiveDescriptorSetCount -= 1; + + SDL_UnlockMutex(descriptorSetPool->lock); + + if (vulkanCommandBuffer->boundDescriptorSetDataCount == vulkanCommandBuffer->boundDescriptorSetDataCapacity) + { + vulkanCommandBuffer->boundDescriptorSetDataCapacity *= 2; + vulkanCommandBuffer->boundDescriptorSetDatas = SDL_realloc( + vulkanCommandBuffer->boundDescriptorSetDatas, + vulkanCommandBuffer->boundDescriptorSetDataCapacity * sizeof(DescriptorSetData) + ); + } + + vulkanCommandBuffer->boundDescriptorSetDatas[vulkanCommandBuffer->boundDescriptorSetDataCount].descriptorSet = descriptorSet; + vulkanCommandBuffer->boundDescriptorSetDatas[vulkanCommandBuffer->boundDescriptorSetDataCount].descriptorSetPool = descriptorSetPool; + vulkanCommandBuffer->boundDescriptorSetDataCount += 1; + + return descriptorSet; +} + +static void VULKAN_INTERNAL_BindGraphicsDescriptorSets( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer +) { + VulkanGraphicsPipelineResourceLayout *resourceLayout; + VkWriteDescriptorSet *writeDescriptorSets; + VkWriteDescriptorSet *currentWriteDescriptorSet; + DescriptorSetPool *descriptorSetPool; + VkDescriptorBufferInfo bufferInfos[MAX_STORAGE_BUFFERS_PER_STAGE]; + VkDescriptorImageInfo imageInfos[MAX_TEXTURE_SAMPLERS_PER_STAGE + MAX_STORAGE_TEXTURES_PER_STAGE]; + Uint32 dynamicOffsets[MAX_UNIFORM_BUFFERS_PER_STAGE]; + Uint32 bufferInfoCount = 0; + Uint32 imageInfoCount = 0; + Uint32 i; + + resourceLayout = &commandBuffer->currentGraphicsPipeline->resourceLayout; + + if (commandBuffer->needNewVertexResourceDescriptorSet) + { + descriptorSetPool = &resourceLayout->descriptorSetPools[0]; + + commandBuffer->vertexResourceDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet( + renderer, + commandBuffer, + descriptorSetPool + ); + + writeDescriptorSets = SDL_stack_alloc( + VkWriteDescriptorSet, + resourceLayout->vertexSamplerCount + + resourceLayout->vertexStorageTextureCount + + resourceLayout->vertexStorageBufferCount + ); + + for (i = 0; i < resourceLayout->vertexSamplerCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[i]; + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = i; + currentWriteDescriptorSet->dstSet = commandBuffer->vertexResourceDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pBufferInfo = NULL; + + imageInfos[imageInfoCount].sampler = commandBuffer->vertexSamplers[i]->sampler; + imageInfos[imageInfoCount].imageView = commandBuffer->vertexSamplerTextures[i]->view; + imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount]; + + imageInfoCount += 1; + } + + for (i = 0; i < resourceLayout->vertexStorageTextureCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->vertexSamplerCount + i]; + + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = resourceLayout->vertexSamplerCount + i; + currentWriteDescriptorSet->dstSet = commandBuffer->vertexResourceDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pBufferInfo = NULL; + + imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE; + imageInfos[imageInfoCount].imageView = commandBuffer->vertexStorageTextureSlices[i]->view; + imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount]; + + imageInfoCount += 1; + } + + for (i = 0; i < resourceLayout->vertexStorageBufferCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->vertexSamplerCount + resourceLayout->vertexStorageTextureCount + i]; + + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = resourceLayout->vertexSamplerCount + resourceLayout->vertexStorageTextureCount + i; + currentWriteDescriptorSet->dstSet = commandBuffer->vertexResourceDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pImageInfo = NULL; + + bufferInfos[bufferInfoCount].buffer = commandBuffer->vertexStorageBuffers[i]->buffer; + bufferInfos[bufferInfoCount].offset = 0; + bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE; + + currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount]; + + bufferInfoCount += 1; + } + + renderer->vkUpdateDescriptorSets( + renderer->logicalDevice, + resourceLayout->vertexSamplerCount + resourceLayout->vertexStorageTextureCount + resourceLayout->vertexStorageBufferCount, + writeDescriptorSets, + 0, + NULL + ); + + renderer->vkCmdBindDescriptorSets( + commandBuffer->commandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + resourceLayout->pipelineLayout, + 0, + 1, + &commandBuffer->vertexResourceDescriptorSet, + 0, + NULL + ); + + SDL_stack_free(writeDescriptorSets); + bufferInfoCount = 0; + imageInfoCount = 0; + + commandBuffer->needNewVertexResourceDescriptorSet = SDL_FALSE; + } + + if (commandBuffer->needNewVertexUniformDescriptorSet) + { + descriptorSetPool = &resourceLayout->descriptorSetPools[1]; + + commandBuffer->vertexUniformDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet( + renderer, + commandBuffer, + descriptorSetPool + ); + + writeDescriptorSets = SDL_stack_alloc( + VkWriteDescriptorSet, + resourceLayout->vertexUniformBufferCount + ); + + for (i = 0; i < resourceLayout->vertexUniformBufferCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[i]; + + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = i; + currentWriteDescriptorSet->dstSet = commandBuffer->vertexUniformDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pImageInfo = NULL; + + bufferInfos[bufferInfoCount].buffer = commandBuffer->vertexUniformBuffers[i]->bufferContainer->activeBufferHandle->vulkanBuffer->buffer; + bufferInfos[bufferInfoCount].offset = 0; + bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE; + + currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount]; + + bufferInfoCount += 1; + } + + renderer->vkUpdateDescriptorSets( + renderer->logicalDevice, + resourceLayout->vertexUniformBufferCount, + writeDescriptorSets, + 0, + NULL + ); + + SDL_stack_free(writeDescriptorSets); + bufferInfoCount = 0; + imageInfoCount = 0; + + commandBuffer->needNewVertexUniformDescriptorSet = SDL_FALSE; + commandBuffer->needNewVertexUniformOffsets = SDL_TRUE; + } + + if (commandBuffer->needNewVertexUniformOffsets) + { + for (i = 0; i < resourceLayout->vertexUniformBufferCount; i += 1) + { + dynamicOffsets[i] = commandBuffer->vertexUniformBuffers[i]->drawOffset; + } + + renderer->vkCmdBindDescriptorSets( + commandBuffer->commandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + resourceLayout->pipelineLayout, + 1, + 1, + &commandBuffer->vertexUniformDescriptorSet, + resourceLayout->vertexUniformBufferCount, + dynamicOffsets + ); + + commandBuffer->needNewVertexUniformOffsets = SDL_FALSE; + } + + if (commandBuffer->needNewFragmentResourceDescriptorSet) + { + descriptorSetPool = &resourceLayout->descriptorSetPools[2]; + + commandBuffer->fragmentResourceDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet( + renderer, + commandBuffer, + descriptorSetPool + ); + + writeDescriptorSets = SDL_stack_alloc( + VkWriteDescriptorSet, + resourceLayout->fragmentSamplerCount + + resourceLayout->fragmentStorageTextureCount + + resourceLayout->fragmentStorageBufferCount + ); + + for (i = 0; i < resourceLayout->fragmentSamplerCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[i]; + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = i; + currentWriteDescriptorSet->dstSet = commandBuffer->fragmentResourceDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pBufferInfo = NULL; + + imageInfos[imageInfoCount].sampler = commandBuffer->fragmentSamplers[i]->sampler; + imageInfos[imageInfoCount].imageView = commandBuffer->fragmentSamplerTextures[i]->view; + imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount]; + + imageInfoCount += 1; + } + + for (i = 0; i < resourceLayout->fragmentStorageTextureCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->fragmentSamplerCount + i]; + + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = resourceLayout->fragmentSamplerCount + i; + currentWriteDescriptorSet->dstSet = commandBuffer->fragmentResourceDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pBufferInfo = NULL; + + imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE; + imageInfos[imageInfoCount].imageView = commandBuffer->fragmentStorageTextureSlices[i]->view; + imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount]; + + imageInfoCount += 1; + } + + for (i = 0; i < resourceLayout->fragmentStorageBufferCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->fragmentSamplerCount + resourceLayout->fragmentStorageTextureCount + i]; + + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = resourceLayout->fragmentSamplerCount + resourceLayout->fragmentStorageTextureCount + i; + currentWriteDescriptorSet->dstSet = commandBuffer->fragmentResourceDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pImageInfo = NULL; + + bufferInfos[bufferInfoCount].buffer = commandBuffer->fragmentStorageBuffers[i]->buffer; + bufferInfos[bufferInfoCount].offset = 0; + bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE; + + currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount]; + + bufferInfoCount += 1; + } + + renderer->vkUpdateDescriptorSets( + renderer->logicalDevice, + resourceLayout->fragmentSamplerCount + resourceLayout->fragmentStorageTextureCount + resourceLayout->fragmentStorageBufferCount, + writeDescriptorSets, + 0, + NULL + ); + + renderer->vkCmdBindDescriptorSets( + commandBuffer->commandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + resourceLayout->pipelineLayout, + 2, + 1, + &commandBuffer->fragmentResourceDescriptorSet, + 0, + NULL + ); + + SDL_stack_free(writeDescriptorSets); + bufferInfoCount = 0; + imageInfoCount = 0; + + commandBuffer->needNewFragmentResourceDescriptorSet = SDL_TRUE; + } + + if (commandBuffer->needNewFragmentUniformDescriptorSet) + { + descriptorSetPool = &resourceLayout->descriptorSetPools[3]; + + commandBuffer->fragmentUniformDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet( + renderer, + commandBuffer, + descriptorSetPool + ); + + writeDescriptorSets = SDL_stack_alloc( + VkWriteDescriptorSet, + resourceLayout->fragmentUniformBufferCount + ); + + for (i = 0; i < resourceLayout->fragmentUniformBufferCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[i]; + + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = i; + currentWriteDescriptorSet->dstSet = commandBuffer->fragmentUniformDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pImageInfo = NULL; + + bufferInfos[bufferInfoCount].buffer = commandBuffer->fragmentUniformBuffers[i]->bufferContainer->activeBufferHandle->vulkanBuffer->buffer; + bufferInfos[bufferInfoCount].offset = 0; + bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE; + + currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount]; + + bufferInfoCount += 1; + } + + renderer->vkUpdateDescriptorSets( + renderer->logicalDevice, + resourceLayout->fragmentUniformBufferCount, + writeDescriptorSets, + 0, + NULL + ); + + SDL_stack_free(writeDescriptorSets); + bufferInfoCount = 0; + imageInfoCount = 0; + + commandBuffer->needNewFragmentUniformDescriptorSet = SDL_FALSE; + commandBuffer->needNewFragmentUniformOffsets = SDL_TRUE; + } + + if (commandBuffer->needNewFragmentUniformOffsets) + { + for (i = 0; i < resourceLayout->fragmentUniformBufferCount; i += 1) + { + dynamicOffsets[i] = commandBuffer->fragmentUniformBuffers[i]->drawOffset; + } + + renderer->vkCmdBindDescriptorSets( + commandBuffer->commandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + resourceLayout->pipelineLayout, + 3, + 1, + &commandBuffer->fragmentUniformDescriptorSet, + resourceLayout->fragmentUniformBufferCount, + dynamicOffsets + ); + + commandBuffer->needNewFragmentUniformOffsets = SDL_FALSE; + } +} + +static void VULKAN_DrawIndexedPrimitives( + Refresh_CommandBuffer *commandBuffer, + Uint32 baseVertex, + Uint32 startIndex, + Uint32 primitiveCount, + Uint32 instanceCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer* renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + + VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer); + + renderer->vkCmdDrawIndexed( + vulkanCommandBuffer->commandBuffer, + PrimitiveVerts( + vulkanCommandBuffer->currentGraphicsPipeline->primitiveType, + primitiveCount + ), + instanceCount, + startIndex, + baseVertex, + 0 + ); +} + +static void VULKAN_DrawPrimitives( + Refresh_CommandBuffer *commandBuffer, + Uint32 vertexStart, + Uint32 primitiveCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer* renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + + VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer); + + renderer->vkCmdDraw( + vulkanCommandBuffer->commandBuffer, + PrimitiveVerts( + vulkanCommandBuffer->currentGraphicsPipeline->primitiveType, + primitiveCount + ), + 1, + vertexStart, + 0 + ); +} + +static void VULKAN_DrawPrimitivesIndirect( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *buffer, + Uint32 offsetInBytes, + Uint32 drawCount, + Uint32 stride +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer* renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer*) buffer)->activeBufferHandle->vulkanBuffer; + + VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer); + + renderer->vkCmdDrawIndirect( + vulkanCommandBuffer->commandBuffer, + vulkanBuffer->buffer, + offsetInBytes, + drawCount, + stride + ); + + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, vulkanBuffer); +} + +static void VULKAN_DrawIndexedPrimitivesIndirect( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *buffer, + Uint32 offsetInBytes, + Uint32 drawCount, + Uint32 stride +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer* renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer*) buffer)->activeBufferHandle->vulkanBuffer; + + VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer); + + renderer->vkCmdDrawIndexedIndirect( + vulkanCommandBuffer->commandBuffer, + vulkanBuffer->buffer, + offsetInBytes, + drawCount, + stride + ); + + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, vulkanBuffer); +} + +/* Debug Naming */ + +static void VULKAN_INTERNAL_SetBufferName( + VulkanRenderer *renderer, + VulkanBuffer *buffer, + const char *text +) { + VkDebugUtilsObjectNameInfoEXT nameInfo; + + if (renderer->debugMode && renderer->supportsDebugUtils) + { + nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; + nameInfo.pNext = NULL; + nameInfo.pObjectName = text; + nameInfo.objectType = VK_OBJECT_TYPE_BUFFER; + nameInfo.objectHandle = (uint64_t) buffer->buffer; + + renderer->vkSetDebugUtilsObjectNameEXT( + renderer->logicalDevice, + &nameInfo + ); + } +} + +static void VULKAN_SetBufferName( + Refresh_Renderer *driverData, + Refresh_Buffer *buffer, + const char *text +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanBufferContainer *container = (VulkanBufferContainer*) buffer; + + if (renderer->debugMode && renderer->supportsDebugUtils) + { + container->debugName = SDL_realloc( + container->debugName, + SDL_strlen(text) + 1 + ); + + SDL_utf8strlcpy( + container->debugName, + text, + SDL_strlen(text) + 1 + ); + + for (Uint32 i = 0; i < container->bufferCount; i += 1) + { + VULKAN_INTERNAL_SetBufferName( + renderer, + container->bufferHandles[i]->vulkanBuffer, + text + ); + } + } +} + +static void VULKAN_INTERNAL_SetTextureName( + VulkanRenderer *renderer, + VulkanTexture *texture, + const char *text +) { + VkDebugUtilsObjectNameInfoEXT nameInfo; + + if (renderer->debugMode && renderer->supportsDebugUtils) + { + nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; + nameInfo.pNext = NULL; + nameInfo.pObjectName = text; + nameInfo.objectType = VK_OBJECT_TYPE_IMAGE; + nameInfo.objectHandle = (uint64_t) texture->image; + + renderer->vkSetDebugUtilsObjectNameEXT( + renderer->logicalDevice, + &nameInfo + ); + } +} + +static void VULKAN_SetTextureName( + Refresh_Renderer *driverData, + Refresh_Texture *texture, + const char *text +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanTextureContainer *container = (VulkanTextureContainer*) texture; + + if (renderer->debugMode && renderer->supportsDebugUtils) + { + container->debugName = SDL_realloc( + container->debugName, + SDL_strlen(text) + 1 + ); + + SDL_utf8strlcpy( + container->debugName, + text, + SDL_strlen(text) + 1 + ); + + for (Uint32 i = 0; i < container->textureCount; i += 1) + { + VULKAN_INTERNAL_SetTextureName( + renderer, + container->textureHandles[i]->vulkanTexture, + text + ); + } + } +} + +static void VULKAN_SetStringMarker( + Refresh_CommandBuffer *commandBuffer, + const char *text +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer* renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VkDebugUtilsLabelEXT labelInfo; + + if (renderer->supportsDebugUtils) + { + labelInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; + labelInfo.pNext = NULL; + labelInfo.pLabelName = text; + + renderer->vkCmdInsertDebugUtilsLabelEXT( + vulkanCommandBuffer->commandBuffer, + &labelInfo + ); + } +} + +static VulkanTextureHandle* VULKAN_INTERNAL_CreateTextureHandle( + VulkanRenderer *renderer, + Uint32 width, + Uint32 height, + Uint32 depth, + Uint32 isCube, + Uint32 layerCount, + Uint32 levelCount, + VkSampleCountFlagBits sampleCount, + VkFormat format, + VkComponentMapping swizzle, + VkImageAspectFlags aspectMask, + Refresh_TextureUsageFlags textureUsageFlags +) { + VulkanTextureHandle *textureHandle; + VulkanTexture *texture; + + texture = VULKAN_INTERNAL_CreateTexture( + renderer, + width, + height, + depth, + isCube, + layerCount, + levelCount, + sampleCount, + format, + swizzle, + aspectMask, + textureUsageFlags + ); + + if (texture == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture!"); + return NULL; + } + + textureHandle = SDL_malloc(sizeof(VulkanTextureHandle)); + textureHandle->vulkanTexture = texture; + textureHandle->container = NULL; + + texture->handle = textureHandle; + + return textureHandle; +} + +static VulkanTexture* VULKAN_INTERNAL_CreateTexture( + VulkanRenderer *renderer, + Uint32 width, + Uint32 height, + Uint32 depth, + Uint32 isCube, + Uint32 layerCount, + Uint32 levelCount, + VkSampleCountFlagBits sampleCount, + VkFormat format, + VkComponentMapping swizzle, + VkImageAspectFlags aspectMask, + Refresh_TextureUsageFlags textureUsageFlags +) { + VkResult vulkanResult; + VkImageCreateInfo imageCreateInfo; + VkImageCreateFlags imageCreateFlags = 0; + VkImageViewCreateInfo imageViewCreateInfo; + Uint8 bindResult; + Uint8 is3D = depth > 1 ? 1 : 0; + Uint8 isRenderTarget = + ((textureUsageFlags & REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT) != 0) || + ((textureUsageFlags & REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) != 0); + Uint32 i, j, sliceIndex; + VkImageUsageFlags vkUsageFlags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + VulkanTexture *texture = SDL_malloc(sizeof(VulkanTexture)); + + texture->isCube = 0; + texture->is3D = 0; + texture->isRenderTarget = isRenderTarget; + texture->markedForDestroy = 0; + + if (isCube) + { + imageCreateFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + texture->isCube = 1; + } + else if (is3D) + { + imageCreateFlags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT; + texture->is3D = 1; + } + + if (textureUsageFlags & REFRESH_TEXTUREUSAGE_SAMPLER_BIT) + { + vkUsageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT; + } + if (textureUsageFlags & REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT) + { + vkUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + if (textureUsageFlags & REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) + { + vkUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + if (textureUsageFlags & ( + REFRESH_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT | + REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT | + REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT + )) { + vkUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT; + } + + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.pNext = NULL; + imageCreateInfo.flags = imageCreateFlags; + imageCreateInfo.imageType = is3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.extent.width = width; + imageCreateInfo.extent.height = height; + imageCreateInfo.extent.depth = depth; + imageCreateInfo.mipLevels = levelCount; + imageCreateInfo.arrayLayers = layerCount; + imageCreateInfo.samples = VULKAN_INTERNAL_IsVulkanDepthFormat(format) ? sampleCount : VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.usage = vkUsageFlags; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.queueFamilyIndexCount = 0; + imageCreateInfo.pQueueFamilyIndices = NULL; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + vulkanResult = renderer->vkCreateImage( + renderer->logicalDevice, + &imageCreateInfo, + NULL, + &texture->image + ); + VULKAN_ERROR_CHECK(vulkanResult, vkCreateImage, 0) + + bindResult = VULKAN_INTERNAL_BindMemoryForImage( + renderer, + texture->image, + &texture->usedRegion + ); + + if (bindResult != 1) + { + renderer->vkDestroyImage( + renderer->logicalDevice, + texture->image, + NULL); + + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to bind memory for texture!"); + return NULL; + } + + texture->usedRegion->vulkanTexture = texture; /* lol */ + + imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewCreateInfo.pNext = NULL; + imageViewCreateInfo.flags = 0; + imageViewCreateInfo.image = texture->image; + imageViewCreateInfo.format = format; + imageViewCreateInfo.components = swizzle; + imageViewCreateInfo.subresourceRange.aspectMask = aspectMask; + imageViewCreateInfo.subresourceRange.baseMipLevel = 0; + imageViewCreateInfo.subresourceRange.levelCount = levelCount; + imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; + imageViewCreateInfo.subresourceRange.layerCount = layerCount; + + if (isCube) + { + imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE; + } + else if (is3D) + { + imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_3D; + } + else if (layerCount > 1) + { + imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; + } + else + { + imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + } + + vulkanResult = renderer->vkCreateImageView( + renderer->logicalDevice, + &imageViewCreateInfo, + NULL, + &texture->view + ); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreateImageView", vulkanResult); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture image view"); + return NULL; + } + + texture->dimensions.width = width; + texture->dimensions.height = height; + texture->depth = depth; + texture->format = format; + texture->swizzle = swizzle; + texture->levelCount = levelCount; + texture->layerCount = layerCount; + texture->sampleCount = sampleCount; + texture->usageFlags = textureUsageFlags; + texture->aspectFlags = aspectMask; + + /* Define slices */ + texture->sliceCount = + texture->layerCount * + texture->levelCount; + + texture->slices = SDL_malloc( + texture->sliceCount * sizeof(VulkanTextureSlice) + ); + + for (i = 0; i < texture->layerCount; i += 1) + { + for (j = 0; j < texture->levelCount; j += 1) + { + sliceIndex = VULKAN_INTERNAL_GetTextureSliceIndex( + texture, + i, + j + ); + + VULKAN_INTERNAL_CreateSliceView( + renderer, + texture, + i, + j, + swizzle, + &texture->slices[sliceIndex].view + ); + + texture->slices[sliceIndex].parent = texture; + texture->slices[sliceIndex].layer = i; + texture->slices[sliceIndex].level = j; + texture->slices[sliceIndex].msaaTexHandle = NULL; + texture->slices[sliceIndex].transitioned = SDL_FALSE; + SDL_AtomicSet(&texture->slices[sliceIndex].referenceCount, 0); + + if ( + sampleCount > VK_SAMPLE_COUNT_1_BIT && + isRenderTarget && + !VULKAN_INTERNAL_IsVulkanDepthFormat(texture->format) + ) { + texture->slices[sliceIndex].msaaTexHandle = VULKAN_INTERNAL_CreateTextureHandle( + renderer, + texture->dimensions.width >> j, + texture->dimensions.height >> j, + 1, + 0, + 1, + 1, + VK_SAMPLE_COUNT_1_BIT, + texture->format, + texture->swizzle, + aspectMask, + textureUsageFlags + ); + } + } + } + + return texture; +} + +static void VULKAN_INTERNAL_CycleActiveBuffer( + VulkanRenderer *renderer, + VulkanBufferContainer *bufferContainer +) { + VulkanBufferHandle *bufferHandle; + Uint32 i; + + /* If a previously-cycled buffer is available, we can use that. */ + for (i = 0; i < bufferContainer->bufferCount; i += 1) + { + bufferHandle = bufferContainer->bufferHandles[i]; + if (SDL_AtomicGet(&bufferHandle->vulkanBuffer->referenceCount) == 0) + { + bufferContainer->activeBufferHandle = bufferHandle; + return; + } + } + + /* No buffer handle is available, generate a new one. */ + bufferContainer->activeBufferHandle = VULKAN_INTERNAL_CreateBufferHandle( + renderer, + bufferContainer->activeBufferHandle->vulkanBuffer->size, + bufferContainer->activeBufferHandle->vulkanBuffer->usageFlags, + bufferContainer->activeBufferHandle->vulkanBuffer->type + ); + + bufferContainer->activeBufferHandle->container = bufferContainer; + + EXPAND_ARRAY_IF_NEEDED( + bufferContainer->bufferHandles, + VulkanBufferHandle*, + bufferContainer->bufferCount + 1, + bufferContainer->bufferCapacity, + bufferContainer->bufferCapacity * 2 + ); + + bufferContainer->bufferHandles[ + bufferContainer->bufferCount + ] = bufferContainer->activeBufferHandle; + bufferContainer->bufferCount += 1; + + if ( + renderer->debugMode && + renderer->supportsDebugUtils && + bufferContainer->debugName != NULL + ) { + VULKAN_INTERNAL_SetBufferName( + renderer, + bufferContainer->activeBufferHandle->vulkanBuffer, + bufferContainer->debugName + ); + } +} + +static void VULKAN_INTERNAL_CycleActiveTexture( + VulkanRenderer *renderer, + VulkanTextureContainer *textureContainer +) { + VulkanTextureHandle *textureHandle; + Uint32 i, j; + Sint32 refCountTotal; + + /* If a previously-cycled texture is available, we can use that. */ + for (i = 0; i < textureContainer->textureCount; i += 1) + { + textureHandle = textureContainer->textureHandles[i]; + + refCountTotal = 0; + for (j = 0; j < textureHandle->vulkanTexture->sliceCount; j += 1) + { + refCountTotal += SDL_AtomicGet(&textureHandle->vulkanTexture->slices[j].referenceCount); + } + + if (refCountTotal == 0) + { + textureContainer->activeTextureHandle = textureHandle; + return; + } + } + + /* No texture handle is available, generate a new one. */ + textureContainer->activeTextureHandle = VULKAN_INTERNAL_CreateTextureHandle( + renderer, + textureContainer->activeTextureHandle->vulkanTexture->dimensions.width, + textureContainer->activeTextureHandle->vulkanTexture->dimensions.height, + textureContainer->activeTextureHandle->vulkanTexture->depth, + textureContainer->activeTextureHandle->vulkanTexture->isCube, + textureContainer->activeTextureHandle->vulkanTexture->layerCount, + textureContainer->activeTextureHandle->vulkanTexture->levelCount, + textureContainer->activeTextureHandle->vulkanTexture->sampleCount, + textureContainer->activeTextureHandle->vulkanTexture->format, + textureContainer->activeTextureHandle->vulkanTexture->swizzle, + textureContainer->activeTextureHandle->vulkanTexture->aspectFlags, + textureContainer->activeTextureHandle->vulkanTexture->usageFlags + ); + + textureContainer->activeTextureHandle->container = textureContainer; + + EXPAND_ARRAY_IF_NEEDED( + textureContainer->textureHandles, + VulkanTextureHandle*, + textureContainer->textureCount + 1, + textureContainer->textureCapacity, + textureContainer->textureCapacity * 2 + ); + + textureContainer->textureHandles[ + textureContainer->textureCount + ] = textureContainer->activeTextureHandle; + textureContainer->textureCount += 1; + + if ( + renderer->debugMode && + renderer->supportsDebugUtils && + textureContainer->debugName != NULL + ) { + VULKAN_INTERNAL_SetTextureName( + renderer, + textureContainer->activeTextureHandle->vulkanTexture, + textureContainer->debugName + ); + } +} + +static VulkanBuffer* VULKAN_INTERNAL_PrepareBufferForWrite( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanBufferContainer *bufferContainer, + SDL_bool cycle, + VulkanBufferUsageMode destinationUsageMode +) { + if ( + cycle && + SDL_AtomicGet(&bufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0 + ) { + VULKAN_INTERNAL_CycleActiveBuffer( + renderer, + bufferContainer + ); + } + + VULKAN_INTERNAL_BufferTransitionFromDefaultUsage( + renderer, + commandBuffer, + destinationUsageMode, + bufferContainer->activeBufferHandle->vulkanBuffer + ); + + return bufferContainer->activeBufferHandle->vulkanBuffer; +} + +static VulkanTextureSlice* VULKAN_INTERNAL_PrepareTextureSliceForWrite( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanTextureContainer *textureContainer, + Uint32 layer, + Uint32 level, + SDL_bool cycle, + VulkanTextureUsageMode destinationUsageMode +) { + VulkanTextureSlice *textureSlice = VULKAN_INTERNAL_FetchTextureSlice( + textureContainer->activeTextureHandle->vulkanTexture, + layer, + level + ); + + if ( + cycle && + textureContainer->canBeCycled && + SDL_AtomicGet(&textureSlice->referenceCount) > 0 + ) { + VULKAN_INTERNAL_CycleActiveTexture( + renderer, + textureContainer + ); + + textureSlice = VULKAN_INTERNAL_FetchTextureSlice( + textureContainer->activeTextureHandle->vulkanTexture, + layer, + level + ); + } + + /* always do barrier because of layout transitions */ + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + commandBuffer, + destinationUsageMode, + textureSlice + ); + + return textureSlice; +} + +static VkRenderPass VULKAN_INTERNAL_CreateRenderPass( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + Refresh_ColorAttachmentInfo *colorAttachmentInfos, + Uint32 colorAttachmentCount, + Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo +) { + VkResult vulkanResult; + VkAttachmentDescription attachmentDescriptions[2 * MAX_COLOR_TARGET_BINDINGS + 1]; + VkAttachmentReference colorAttachmentReferences[MAX_COLOR_TARGET_BINDINGS]; + VkAttachmentReference resolveReferences[MAX_COLOR_TARGET_BINDINGS + 1]; + VkAttachmentReference depthStencilAttachmentReference; + VkRenderPassCreateInfo renderPassCreateInfo; + VkSubpassDescription subpass; + VkRenderPass renderPass; + Uint32 i; + + Uint32 attachmentDescriptionCount = 0; + Uint32 colorAttachmentReferenceCount = 0; + Uint32 resolveReferenceCount = 0; + + VulkanTexture *texture = NULL; + + for (i = 0; i < colorAttachmentCount; i += 1) + { + texture = ((VulkanTextureContainer*) colorAttachmentInfos[i].textureSlice.texture)->activeTextureHandle->vulkanTexture; + + if (texture->sampleCount > VK_SAMPLE_COUNT_1_BIT) + { + /* Resolve attachment and multisample attachment */ + + attachmentDescriptions[attachmentDescriptionCount].flags = 0; + attachmentDescriptions[attachmentDescriptionCount].format = texture->format; + attachmentDescriptions[attachmentDescriptionCount].samples = + VK_SAMPLE_COUNT_1_BIT; + attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[ + colorAttachmentInfos[i].loadOp + ]; + attachmentDescriptions[attachmentDescriptionCount].storeOp = + VK_ATTACHMENT_STORE_OP_STORE; /* Always store the resolve texture */ + attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = + VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = + VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].initialLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentDescriptions[attachmentDescriptionCount].finalLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + resolveReferences[resolveReferenceCount].attachment = + attachmentDescriptionCount; + resolveReferences[resolveReferenceCount].layout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + attachmentDescriptionCount += 1; + resolveReferenceCount += 1; + + attachmentDescriptions[attachmentDescriptionCount].flags = 0; + attachmentDescriptions[attachmentDescriptionCount].format = texture->format; + attachmentDescriptions[attachmentDescriptionCount].samples = texture->sampleCount; + attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[ + colorAttachmentInfos[i].loadOp + ]; + attachmentDescriptions[attachmentDescriptionCount].storeOp = SDLToVK_StoreOp[ + colorAttachmentInfos[i].storeOp + ]; + attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = + VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = + VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].initialLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentDescriptions[attachmentDescriptionCount].finalLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + colorAttachmentReferences[colorAttachmentReferenceCount].attachment = + attachmentDescriptionCount; + colorAttachmentReferences[colorAttachmentReferenceCount].layout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + attachmentDescriptionCount += 1; + colorAttachmentReferenceCount += 1; + } + else + { + attachmentDescriptions[attachmentDescriptionCount].flags = 0; + attachmentDescriptions[attachmentDescriptionCount].format = texture->format; + attachmentDescriptions[attachmentDescriptionCount].samples = + VK_SAMPLE_COUNT_1_BIT; + attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[ + colorAttachmentInfos[i].loadOp + ]; + attachmentDescriptions[attachmentDescriptionCount].storeOp = + VK_ATTACHMENT_STORE_OP_STORE; /* Always store non-MSAA textures */ + attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = + VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = + VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].initialLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentDescriptions[attachmentDescriptionCount].finalLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + + colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount; + colorAttachmentReferences[colorAttachmentReferenceCount].layout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + attachmentDescriptionCount += 1; + colorAttachmentReferenceCount += 1; + } + } + + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = NULL; + subpass.colorAttachmentCount = colorAttachmentCount; + subpass.pColorAttachments = colorAttachmentReferences; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = NULL; + + if (depthStencilAttachmentInfo == NULL) + { + subpass.pDepthStencilAttachment = NULL; + } + else + { + texture = ((VulkanTextureContainer*) depthStencilAttachmentInfo->textureSlice.texture)->activeTextureHandle->vulkanTexture; + + attachmentDescriptions[attachmentDescriptionCount].flags = 0; + attachmentDescriptions[attachmentDescriptionCount].format = texture->format; + attachmentDescriptions[attachmentDescriptionCount].samples = texture->sampleCount; + + attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[ + depthStencilAttachmentInfo->loadOp + ]; + attachmentDescriptions[attachmentDescriptionCount].storeOp = SDLToVK_StoreOp[ + depthStencilAttachmentInfo->storeOp + ]; + attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = SDLToVK_LoadOp[ + depthStencilAttachmentInfo->stencilLoadOp + ]; + attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = SDLToVK_StoreOp[ + depthStencilAttachmentInfo->stencilStoreOp + ]; + attachmentDescriptions[attachmentDescriptionCount].initialLayout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachmentDescriptions[attachmentDescriptionCount].finalLayout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + depthStencilAttachmentReference.attachment = + attachmentDescriptionCount; + depthStencilAttachmentReference.layout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + subpass.pDepthStencilAttachment = + &depthStencilAttachmentReference; + + attachmentDescriptionCount += 1; + } + + if (texture != NULL && texture->sampleCount > VK_SAMPLE_COUNT_1_BIT) + { + subpass.pResolveAttachments = resolveReferences; + } + else + { + subpass.pResolveAttachments = NULL; + } + + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.pNext = NULL; + renderPassCreateInfo.flags = 0; + renderPassCreateInfo.pAttachments = attachmentDescriptions; + renderPassCreateInfo.attachmentCount = attachmentDescriptionCount; + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subpass; + renderPassCreateInfo.dependencyCount = 0; + renderPassCreateInfo.pDependencies = NULL; + + vulkanResult = renderer->vkCreateRenderPass( + renderer->logicalDevice, + &renderPassCreateInfo, + NULL, + &renderPass + ); + + if (vulkanResult != VK_SUCCESS) + { + renderPass = VK_NULL_HANDLE; + LogVulkanResultAsError("vkCreateRenderPass", vulkanResult); + } + + return renderPass; +} + +static VkRenderPass VULKAN_INTERNAL_CreateTransientRenderPass( + VulkanRenderer *renderer, + Refresh_GraphicsPipelineAttachmentInfo attachmentInfo, + VkSampleCountFlagBits sampleCount +) { + VkAttachmentDescription attachmentDescriptions[2 * MAX_COLOR_TARGET_BINDINGS + 1]; + VkAttachmentReference colorAttachmentReferences[MAX_COLOR_TARGET_BINDINGS]; + VkAttachmentReference resolveReferences[MAX_COLOR_TARGET_BINDINGS + 1]; + VkAttachmentReference depthStencilAttachmentReference; + Refresh_ColorAttachmentDescription attachmentDescription; + VkSubpassDescription subpass; + VkRenderPassCreateInfo renderPassCreateInfo; + VkRenderPass renderPass; + VkResult result; + + Uint32 multisampling = 0; + Uint32 attachmentDescriptionCount = 0; + Uint32 colorAttachmentReferenceCount = 0; + Uint32 resolveReferenceCount = 0; + Uint32 i; + + for (i = 0; i < attachmentInfo.colorAttachmentCount; i += 1) + { + attachmentDescription = attachmentInfo.colorAttachmentDescriptions[i]; + + if (sampleCount > VK_SAMPLE_COUNT_1_BIT) + { + multisampling = 1; + + /* Resolve attachment and multisample attachment */ + + attachmentDescriptions[attachmentDescriptionCount].flags = 0; + attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_SurfaceFormat[ + attachmentDescription.format + ]; + attachmentDescriptions[attachmentDescriptionCount].samples = VK_SAMPLE_COUNT_1_BIT; + attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + resolveReferences[resolveReferenceCount].attachment = attachmentDescriptionCount; + resolveReferences[resolveReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + attachmentDescriptionCount += 1; + resolveReferenceCount += 1; + + attachmentDescriptions[attachmentDescriptionCount].flags = 0; + attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_SurfaceFormat[ + attachmentDescription.format + ]; + attachmentDescriptions[attachmentDescriptionCount].samples = sampleCount; + + attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + colorAttachmentReferences[colorAttachmentReferenceCount].attachment = + attachmentDescriptionCount; + colorAttachmentReferences[colorAttachmentReferenceCount].layout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + attachmentDescriptionCount += 1; + colorAttachmentReferenceCount += 1; + } + else + { + attachmentDescriptions[attachmentDescriptionCount].flags = 0; + attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_SurfaceFormat[ + attachmentDescription.format + ]; + attachmentDescriptions[attachmentDescriptionCount].samples = + VK_SAMPLE_COUNT_1_BIT; + attachmentDescriptions[attachmentDescriptionCount].loadOp = + VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].storeOp = + VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = + VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = + VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].initialLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentDescriptions[attachmentDescriptionCount].finalLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + + colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount; + colorAttachmentReferences[colorAttachmentReferenceCount].layout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + attachmentDescriptionCount += 1; + colorAttachmentReferenceCount += 1; + } + } + + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = NULL; + subpass.colorAttachmentCount = attachmentInfo.colorAttachmentCount; + subpass.pColorAttachments = colorAttachmentReferences; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = NULL; + + if (attachmentInfo.hasDepthStencilAttachment) + { + attachmentDescriptions[attachmentDescriptionCount].flags = 0; + attachmentDescriptions[attachmentDescriptionCount].format = + SDLToVK_SurfaceFormat[attachmentInfo.depthStencilFormat]; + attachmentDescriptions[attachmentDescriptionCount].samples = sampleCount; + + attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachmentDescriptions[attachmentDescriptionCount].initialLayout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachmentDescriptions[attachmentDescriptionCount].finalLayout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + depthStencilAttachmentReference.attachment = + attachmentDescriptionCount; + depthStencilAttachmentReference.layout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + subpass.pDepthStencilAttachment = + &depthStencilAttachmentReference; + + attachmentDescriptionCount += 1; + } + else + { + subpass.pDepthStencilAttachment = NULL; + } + + if (multisampling) + { + subpass.pResolveAttachments = resolveReferences; + } + else + { + subpass.pResolveAttachments = NULL; + } + + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.pNext = NULL; + renderPassCreateInfo.flags = 0; + renderPassCreateInfo.pAttachments = attachmentDescriptions; + renderPassCreateInfo.attachmentCount = attachmentDescriptionCount; + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subpass; + renderPassCreateInfo.dependencyCount = 0; + renderPassCreateInfo.pDependencies = NULL; + + result = renderer->vkCreateRenderPass( + renderer->logicalDevice, + &renderPassCreateInfo, + NULL, + &renderPass + ); + + if (result != VK_SUCCESS) + { + renderPass = VK_NULL_HANDLE; + LogVulkanResultAsError("vkCreateRenderPass", result); + } + + return renderPass; +} + +static Refresh_GraphicsPipeline* VULKAN_CreateGraphicsPipeline( + Refresh_Renderer *driverData, + Refresh_GraphicsPipelineCreateInfo *pipelineCreateInfo +) { + VkResult vulkanResult; + Uint32 i; + VkSampleCountFlagBits actualSampleCount; + + VulkanGraphicsPipeline *graphicsPipeline = (VulkanGraphicsPipeline*) SDL_malloc(sizeof(VulkanGraphicsPipeline)); + VkGraphicsPipelineCreateInfo vkPipelineCreateInfo; + + VkPipelineShaderStageCreateInfo shaderStageCreateInfos[2]; + + VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo; + VkPipelineVertexInputDivisorStateCreateInfoEXT divisorStateCreateInfo; + VkVertexInputBindingDescription *vertexInputBindingDescriptions = SDL_stack_alloc(VkVertexInputBindingDescription, pipelineCreateInfo->vertexInputState.vertexBindingCount); + VkVertexInputAttributeDescription *vertexInputAttributeDescriptions = SDL_stack_alloc(VkVertexInputAttributeDescription, pipelineCreateInfo->vertexInputState.vertexAttributeCount); + VkVertexInputBindingDivisorDescriptionEXT *divisorDescriptions = SDL_stack_alloc(VkVertexInputBindingDivisorDescriptionEXT, pipelineCreateInfo->vertexInputState.vertexBindingCount); + Uint32 divisorDescriptionCount = 0; + + VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo; + + VkPipelineViewportStateCreateInfo viewportStateCreateInfo; + + VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo; + + VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo; + + VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo; + VkStencilOpState frontStencilState; + VkStencilOpState backStencilState; + + VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo; + VkPipelineColorBlendAttachmentState *colorBlendAttachmentStates = SDL_stack_alloc( + VkPipelineColorBlendAttachmentState, + pipelineCreateInfo->attachmentInfo.colorAttachmentCount + ); + + static const VkDynamicState dynamicStates[] = + { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo; + + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + + /* Find a compatible sample count to use */ + + actualSampleCount = VULKAN_INTERNAL_GetMaxMultiSampleCount( + renderer, + SDLToVK_SampleCount[pipelineCreateInfo->multisampleState.multisampleCount] + ); + + /* Create a "compatible" render pass */ + + VkRenderPass transientRenderPass = VULKAN_INTERNAL_CreateTransientRenderPass( + renderer, + pipelineCreateInfo->attachmentInfo, + actualSampleCount + ); + + /* Dynamic state */ + + dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicStateCreateInfo.pNext = NULL; + dynamicStateCreateInfo.flags = 0; + dynamicStateCreateInfo.dynamicStateCount = SDL_arraysize(dynamicStates); + dynamicStateCreateInfo.pDynamicStates = dynamicStates; + + /* Shader stages */ + + graphicsPipeline->vertexShader = (VulkanShader*) pipelineCreateInfo->vertexShader; + SDL_AtomicIncRef(&graphicsPipeline->vertexShader->referenceCount); + + shaderStageCreateInfos[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStageCreateInfos[0].pNext = NULL; + shaderStageCreateInfos[0].flags = 0; + shaderStageCreateInfos[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + shaderStageCreateInfos[0].module = graphicsPipeline->vertexShader->shaderModule; + shaderStageCreateInfos[0].pName = graphicsPipeline->vertexShader->entryPointName; + shaderStageCreateInfos[0].pSpecializationInfo = NULL; + + graphicsPipeline->fragmentShader = (VulkanShader*) pipelineCreateInfo->fragmentShader; + SDL_AtomicIncRef(&graphicsPipeline->fragmentShader->referenceCount); + + shaderStageCreateInfos[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStageCreateInfos[1].pNext = NULL; + shaderStageCreateInfos[1].flags = 0; + shaderStageCreateInfos[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shaderStageCreateInfos[1].module = graphicsPipeline->fragmentShader->shaderModule; + shaderStageCreateInfos[1].pName = graphicsPipeline->fragmentShader->entryPointName; + shaderStageCreateInfos[1].pSpecializationInfo = NULL; + + /* Vertex input */ + + for (i = 0; i < pipelineCreateInfo->vertexInputState.vertexBindingCount; i += 1) + { + vertexInputBindingDescriptions[i].binding = pipelineCreateInfo->vertexInputState.vertexBindings[i].binding; + vertexInputBindingDescriptions[i].inputRate = SDLToVK_VertexInputRate[ + pipelineCreateInfo->vertexInputState.vertexBindings[i].inputRate + ]; + vertexInputBindingDescriptions[i].stride = pipelineCreateInfo->vertexInputState.vertexBindings[i].stride; + + if (pipelineCreateInfo->vertexInputState.vertexBindings[i].inputRate == REFRESH_VERTEXINPUTRATE_INSTANCE) + { + divisorDescriptionCount += 1; + } + } + + for (i = 0; i < pipelineCreateInfo->vertexInputState.vertexAttributeCount; i += 1) + { + vertexInputAttributeDescriptions[i].binding = pipelineCreateInfo->vertexInputState.vertexAttributes[i].binding; + vertexInputAttributeDescriptions[i].format = SDLToVK_VertexFormat[ + pipelineCreateInfo->vertexInputState.vertexAttributes[i].format + ]; + vertexInputAttributeDescriptions[i].location = pipelineCreateInfo->vertexInputState.vertexAttributes[i].location; + vertexInputAttributeDescriptions[i].offset = pipelineCreateInfo->vertexInputState.vertexAttributes[i].offset; + } + + vertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputStateCreateInfo.pNext = NULL; + vertexInputStateCreateInfo.flags = 0; + vertexInputStateCreateInfo.vertexBindingDescriptionCount = pipelineCreateInfo->vertexInputState.vertexBindingCount; + vertexInputStateCreateInfo.pVertexBindingDescriptions = vertexInputBindingDescriptions; + vertexInputStateCreateInfo.vertexAttributeDescriptionCount = pipelineCreateInfo->vertexInputState.vertexAttributeCount; + vertexInputStateCreateInfo.pVertexAttributeDescriptions = vertexInputAttributeDescriptions; + + if (divisorDescriptionCount > 0) + { + divisorDescriptionCount = 0; + + for (i = 0; i < pipelineCreateInfo->vertexInputState.vertexBindingCount; i += 1) + { + if (pipelineCreateInfo->vertexInputState.vertexBindings[i].inputRate == REFRESH_VERTEXINPUTRATE_INSTANCE) + { + divisorDescriptions[divisorDescriptionCount].binding = pipelineCreateInfo->vertexInputState.vertexBindings[i].binding; + divisorDescriptions[divisorDescriptionCount].divisor = pipelineCreateInfo->vertexInputState.vertexBindings[i].stepRate; + + divisorDescriptionCount += 1; + } + } + + divisorStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT; + divisorStateCreateInfo.pNext = NULL; + divisorStateCreateInfo.vertexBindingDivisorCount = divisorDescriptionCount; + divisorStateCreateInfo.pVertexBindingDivisors = divisorDescriptions; + + vertexInputStateCreateInfo.pNext = &divisorStateCreateInfo; + } + + /* Topology */ + + inputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssemblyStateCreateInfo.pNext = NULL; + inputAssemblyStateCreateInfo.flags = 0; + inputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE; + inputAssemblyStateCreateInfo.topology = SDLToVK_PrimitiveType[ + pipelineCreateInfo->primitiveType + ]; + + graphicsPipeline->primitiveType = pipelineCreateInfo->primitiveType; + + /* Viewport */ + + /* NOTE: viewport and scissor are dynamic, and must be set using the command buffer */ + + viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportStateCreateInfo.pNext = NULL; + viewportStateCreateInfo.flags = 0; + viewportStateCreateInfo.viewportCount = 1; + viewportStateCreateInfo.pViewports = NULL; + viewportStateCreateInfo.scissorCount = 1; + viewportStateCreateInfo.pScissors = NULL; + + /* Rasterization */ + + rasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizationStateCreateInfo.pNext = NULL; + rasterizationStateCreateInfo.flags = 0; + rasterizationStateCreateInfo.depthClampEnable = VK_FALSE; + rasterizationStateCreateInfo.rasterizerDiscardEnable = VK_FALSE; + rasterizationStateCreateInfo.polygonMode = SDLToVK_PolygonMode[ + pipelineCreateInfo->rasterizerState.fillMode + ]; + rasterizationStateCreateInfo.cullMode = SDLToVK_CullMode[ + pipelineCreateInfo->rasterizerState.cullMode + ]; + rasterizationStateCreateInfo.frontFace = SDLToVK_FrontFace[ + pipelineCreateInfo->rasterizerState.frontFace + ]; + rasterizationStateCreateInfo.depthBiasEnable = + pipelineCreateInfo->rasterizerState.depthBiasEnable; + rasterizationStateCreateInfo.depthBiasConstantFactor = + pipelineCreateInfo->rasterizerState.depthBiasConstantFactor; + rasterizationStateCreateInfo.depthBiasClamp = + pipelineCreateInfo->rasterizerState.depthBiasClamp; + rasterizationStateCreateInfo.depthBiasSlopeFactor = + pipelineCreateInfo->rasterizerState.depthBiasSlopeFactor; + rasterizationStateCreateInfo.lineWidth = 1.0f; + + /* Multisample */ + + multisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampleStateCreateInfo.pNext = NULL; + multisampleStateCreateInfo.flags = 0; + multisampleStateCreateInfo.rasterizationSamples = actualSampleCount; + multisampleStateCreateInfo.sampleShadingEnable = VK_FALSE; + multisampleStateCreateInfo.minSampleShading = 1.0f; + multisampleStateCreateInfo.pSampleMask = + &pipelineCreateInfo->multisampleState.sampleMask; + multisampleStateCreateInfo.alphaToCoverageEnable = VK_FALSE; + multisampleStateCreateInfo.alphaToOneEnable = VK_FALSE; + + /* Depth Stencil State */ + + frontStencilState.failOp = SDLToVK_StencilOp[ + pipelineCreateInfo->depthStencilState.frontStencilState.failOp + ]; + frontStencilState.passOp = SDLToVK_StencilOp[ + pipelineCreateInfo->depthStencilState.frontStencilState.passOp + ]; + frontStencilState.depthFailOp = SDLToVK_StencilOp[ + pipelineCreateInfo->depthStencilState.frontStencilState.depthFailOp + ]; + frontStencilState.compareOp = SDLToVK_CompareOp[ + pipelineCreateInfo->depthStencilState.frontStencilState.compareOp + ]; + frontStencilState.compareMask = + pipelineCreateInfo->depthStencilState.compareMask; + frontStencilState.writeMask = + pipelineCreateInfo->depthStencilState.writeMask; + frontStencilState.reference = + pipelineCreateInfo->depthStencilState.reference; + + backStencilState.failOp = SDLToVK_StencilOp[ + pipelineCreateInfo->depthStencilState.backStencilState.failOp + ]; + backStencilState.passOp = SDLToVK_StencilOp[ + pipelineCreateInfo->depthStencilState.backStencilState.passOp + ]; + backStencilState.depthFailOp = SDLToVK_StencilOp[ + pipelineCreateInfo->depthStencilState.backStencilState.depthFailOp + ]; + backStencilState.compareOp = SDLToVK_CompareOp[ + pipelineCreateInfo->depthStencilState.backStencilState.compareOp + ]; + backStencilState.compareMask = + pipelineCreateInfo->depthStencilState.compareMask; + backStencilState.writeMask = + pipelineCreateInfo->depthStencilState.writeMask; + backStencilState.reference = + pipelineCreateInfo->depthStencilState.reference; + + + depthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencilStateCreateInfo.pNext = NULL; + depthStencilStateCreateInfo.flags = 0; + depthStencilStateCreateInfo.depthTestEnable = + pipelineCreateInfo->depthStencilState.depthTestEnable; + depthStencilStateCreateInfo.depthWriteEnable = + pipelineCreateInfo->depthStencilState.depthWriteEnable; + depthStencilStateCreateInfo.depthCompareOp = SDLToVK_CompareOp[ + pipelineCreateInfo->depthStencilState.compareOp + ]; + depthStencilStateCreateInfo.depthBoundsTestEnable = + pipelineCreateInfo->depthStencilState.depthBoundsTestEnable; + depthStencilStateCreateInfo.stencilTestEnable = + pipelineCreateInfo->depthStencilState.stencilTestEnable; + depthStencilStateCreateInfo.front = frontStencilState; + depthStencilStateCreateInfo.back = backStencilState; + depthStencilStateCreateInfo.minDepthBounds = + pipelineCreateInfo->depthStencilState.minDepthBounds; + depthStencilStateCreateInfo.maxDepthBounds = + pipelineCreateInfo->depthStencilState.maxDepthBounds; + + /* Color Blend */ + + for (i = 0; i < pipelineCreateInfo->attachmentInfo.colorAttachmentCount; i += 1) + { + Refresh_ColorAttachmentBlendState blendState = pipelineCreateInfo->attachmentInfo.colorAttachmentDescriptions[i].blendState; + + colorBlendAttachmentStates[i].blendEnable = + blendState.blendEnable; + colorBlendAttachmentStates[i].srcColorBlendFactor = SDLToVK_BlendFactor[ + blendState.srcColorBlendFactor + ]; + colorBlendAttachmentStates[i].dstColorBlendFactor = SDLToVK_BlendFactor[ + blendState.dstColorBlendFactor + ]; + colorBlendAttachmentStates[i].colorBlendOp = SDLToVK_BlendOp[ + blendState.colorBlendOp + ]; + colorBlendAttachmentStates[i].srcAlphaBlendFactor = SDLToVK_BlendFactor[ + blendState.srcAlphaBlendFactor + ]; + colorBlendAttachmentStates[i].dstAlphaBlendFactor = SDLToVK_BlendFactor[ + blendState.dstAlphaBlendFactor + ]; + colorBlendAttachmentStates[i].alphaBlendOp = SDLToVK_BlendOp[ + blendState.alphaBlendOp + ]; + colorBlendAttachmentStates[i].colorWriteMask = + blendState.colorWriteMask; + } + + colorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlendStateCreateInfo.pNext = NULL; + colorBlendStateCreateInfo.flags = 0; + colorBlendStateCreateInfo.attachmentCount = + pipelineCreateInfo->attachmentInfo.colorAttachmentCount; + colorBlendStateCreateInfo.pAttachments = + colorBlendAttachmentStates; + colorBlendStateCreateInfo.blendConstants[0] = + pipelineCreateInfo->blendConstants[0]; + colorBlendStateCreateInfo.blendConstants[1] = + pipelineCreateInfo->blendConstants[1]; + colorBlendStateCreateInfo.blendConstants[2] = + pipelineCreateInfo->blendConstants[2]; + colorBlendStateCreateInfo.blendConstants[3] = + pipelineCreateInfo->blendConstants[3]; + + /* We don't support LogicOp, so this is easy. */ + colorBlendStateCreateInfo.logicOpEnable = VK_FALSE; + colorBlendStateCreateInfo.logicOp = 0; + + /* Pipeline Layout */ + + if (!VULKAN_INTERNAL_InitializeGraphicsPipelineResourceLayout( + renderer, + &pipelineCreateInfo->vertexResourceInfo, + &pipelineCreateInfo->fragmentResourceInfo, + &graphicsPipeline->resourceLayout + )) { + SDL_stack_free(vertexInputBindingDescriptions); + SDL_stack_free(vertexInputAttributeDescriptions); + SDL_stack_free(colorBlendAttachmentStates); + SDL_stack_free(divisorDescriptions); + SDL_free(graphicsPipeline); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize pipeline resource layout!"); + return NULL; + } + + /* Pipeline */ + + vkPipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + vkPipelineCreateInfo.pNext = NULL; + vkPipelineCreateInfo.flags = 0; + vkPipelineCreateInfo.stageCount = 2; + vkPipelineCreateInfo.pStages = shaderStageCreateInfos; + vkPipelineCreateInfo.pVertexInputState = &vertexInputStateCreateInfo; + vkPipelineCreateInfo.pInputAssemblyState = &inputAssemblyStateCreateInfo; + vkPipelineCreateInfo.pTessellationState = VK_NULL_HANDLE; + vkPipelineCreateInfo.pViewportState = &viewportStateCreateInfo; + vkPipelineCreateInfo.pRasterizationState = &rasterizationStateCreateInfo; + vkPipelineCreateInfo.pMultisampleState = &multisampleStateCreateInfo; + vkPipelineCreateInfo.pDepthStencilState = &depthStencilStateCreateInfo; + vkPipelineCreateInfo.pColorBlendState = &colorBlendStateCreateInfo; + vkPipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo; + vkPipelineCreateInfo.layout = graphicsPipeline->resourceLayout.pipelineLayout; + vkPipelineCreateInfo.renderPass = transientRenderPass; + vkPipelineCreateInfo.subpass = 0; + vkPipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE; + vkPipelineCreateInfo.basePipelineIndex = 0; + + /* TODO: enable pipeline caching */ + vulkanResult = renderer->vkCreateGraphicsPipelines( + renderer->logicalDevice, + VK_NULL_HANDLE, + 1, + &vkPipelineCreateInfo, + NULL, + &graphicsPipeline->pipeline + ); + + SDL_stack_free(vertexInputBindingDescriptions); + SDL_stack_free(vertexInputAttributeDescriptions); + SDL_stack_free(colorBlendAttachmentStates); + SDL_stack_free(divisorDescriptions); + + renderer->vkDestroyRenderPass( + renderer->logicalDevice, + transientRenderPass, + NULL + ); + + if (vulkanResult != VK_SUCCESS) + { + SDL_free(graphicsPipeline); + LogVulkanResultAsError("vkCreateGraphicsPipelines", vulkanResult); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create graphics pipeline!"); + return NULL; + } + + SDL_AtomicSet(&graphicsPipeline->referenceCount, 0); + + return (Refresh_GraphicsPipeline*) graphicsPipeline; +} + +static Refresh_ComputePipeline* VULKAN_CreateComputePipeline( + Refresh_Renderer *driverData, + Refresh_ComputePipelineCreateInfo *pipelineCreateInfo +) { + VkComputePipelineCreateInfo computePipelineCreateInfo; + VkPipelineShaderStageCreateInfo pipelineShaderStageCreateInfo; + + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanComputePipeline *vulkanComputePipeline = SDL_malloc(sizeof(VulkanComputePipeline)); + + vulkanComputePipeline->computeShader = (VulkanShader*) pipelineCreateInfo->computeShader; + SDL_AtomicIncRef(&vulkanComputePipeline->computeShader->referenceCount); + + pipelineShaderStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + pipelineShaderStageCreateInfo.pNext = NULL; + pipelineShaderStageCreateInfo.flags = 0; + pipelineShaderStageCreateInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; + pipelineShaderStageCreateInfo.module = vulkanComputePipeline->computeShader->shaderModule; + pipelineShaderStageCreateInfo.pName = vulkanComputePipeline->computeShader->entryPointName; + pipelineShaderStageCreateInfo.pSpecializationInfo = NULL; + + if (!VULKAN_INTERNAL_InitializeComputePipelineResourceLayout( + renderer, + &pipelineCreateInfo->pipelineResourceInfo, + &vulkanComputePipeline->resourceLayout + )) + { + SDL_free(vulkanComputePipeline); + return NULL; + } + + computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + computePipelineCreateInfo.pNext = NULL; + computePipelineCreateInfo.flags = 0; + computePipelineCreateInfo.stage = pipelineShaderStageCreateInfo; + computePipelineCreateInfo.layout = + vulkanComputePipeline->resourceLayout.pipelineLayout; + computePipelineCreateInfo.basePipelineHandle = (VkPipeline) VK_NULL_HANDLE; + computePipelineCreateInfo.basePipelineIndex = 0; + + renderer->vkCreateComputePipelines( + renderer->logicalDevice, + (VkPipelineCache) VK_NULL_HANDLE, + 1, + &computePipelineCreateInfo, + NULL, + &vulkanComputePipeline->pipeline + ); + + SDL_AtomicSet(&vulkanComputePipeline->referenceCount, 0); + + return (Refresh_ComputePipeline*) vulkanComputePipeline; +} + +static Refresh_Sampler* VULKAN_CreateSampler( + Refresh_Renderer *driverData, + Refresh_SamplerCreateInfo *samplerCreateInfo +) { + VulkanRenderer* renderer = (VulkanRenderer*)driverData; + VulkanSampler *vulkanSampler = SDL_malloc(sizeof(VulkanSampler)); + VkResult vulkanResult; + + VkSamplerCreateInfo vkSamplerCreateInfo; + vkSamplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + vkSamplerCreateInfo.pNext = NULL; + vkSamplerCreateInfo.flags = 0; + vkSamplerCreateInfo.magFilter = SDLToVK_Filter[ + samplerCreateInfo->magFilter + ]; + vkSamplerCreateInfo.minFilter = SDLToVK_Filter[ + samplerCreateInfo->minFilter + ]; + vkSamplerCreateInfo.mipmapMode = SDLToVK_SamplerMipmapMode[ + samplerCreateInfo->mipmapMode + ]; + vkSamplerCreateInfo.addressModeU = SDLToVK_SamplerAddressMode[ + samplerCreateInfo->addressModeU + ]; + vkSamplerCreateInfo.addressModeV = SDLToVK_SamplerAddressMode[ + samplerCreateInfo->addressModeV + ]; + vkSamplerCreateInfo.addressModeW = SDLToVK_SamplerAddressMode[ + samplerCreateInfo->addressModeW + ]; + vkSamplerCreateInfo.mipLodBias = samplerCreateInfo->mipLodBias; + vkSamplerCreateInfo.anisotropyEnable = samplerCreateInfo->anisotropyEnable; + vkSamplerCreateInfo.maxAnisotropy = samplerCreateInfo->maxAnisotropy; + vkSamplerCreateInfo.compareEnable = samplerCreateInfo->compareEnable; + vkSamplerCreateInfo.compareOp = SDLToVK_CompareOp[ + samplerCreateInfo->compareOp + ]; + vkSamplerCreateInfo.minLod = samplerCreateInfo->minLod; + vkSamplerCreateInfo.maxLod = samplerCreateInfo->maxLod; + vkSamplerCreateInfo.borderColor = SDLToVK_BorderColor[ + samplerCreateInfo->borderColor + ]; + vkSamplerCreateInfo.unnormalizedCoordinates = VK_FALSE; + + vulkanResult = renderer->vkCreateSampler( + renderer->logicalDevice, + &vkSamplerCreateInfo, + NULL, + &vulkanSampler->sampler + ); + + if (vulkanResult != VK_SUCCESS) + { + SDL_free(vulkanSampler); + LogVulkanResultAsError("vkCreateSampler", vulkanResult); + return NULL; + } + + SDL_AtomicSet(&vulkanSampler->referenceCount, 0); + + return (Refresh_Sampler*) vulkanSampler; +} + +static Refresh_Shader* VULKAN_CreateShader( + Refresh_Renderer *driverData, + Refresh_ShaderCreateInfo *shaderCreateInfo +) { + VulkanShader *vulkanShader = SDL_malloc(sizeof(VulkanShader)); + VkResult vulkanResult; + VkShaderModuleCreateInfo vkShaderModuleCreateInfo; + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + + if (shaderCreateInfo->format != REFRESH_SHADERFORMAT_SPIRV) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Incompatible shader format for Vulkan"); + return NULL; + } + + vkShaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + vkShaderModuleCreateInfo.pNext = NULL; + vkShaderModuleCreateInfo.flags = 0; + vkShaderModuleCreateInfo.codeSize = shaderCreateInfo->codeSize; + vkShaderModuleCreateInfo.pCode = (Uint32*) shaderCreateInfo->code; + + vulkanResult = renderer->vkCreateShaderModule( + renderer->logicalDevice, + &vkShaderModuleCreateInfo, + NULL, + &vulkanShader->shaderModule + ); + + if (vulkanResult != VK_SUCCESS) + { + SDL_free(vulkanShader); + LogVulkanResultAsError("vkCreateShaderModule", vulkanResult); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create shader module!"); + return NULL; + } + + vulkanShader->entryPointName = shaderCreateInfo->entryPointName; + + SDL_AtomicSet(&vulkanShader->referenceCount, 0); + + return (Refresh_Shader*) vulkanShader; +} + +static Refresh_Texture* VULKAN_CreateTexture( + Refresh_Renderer *driverData, + Refresh_TextureCreateInfo *textureCreateInfo +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VkImageAspectFlags imageAspectFlags; + Uint8 isDepthFormat = IsDepthFormat(textureCreateInfo->format); + VkFormat format; + VulkanTextureContainer *container; + VulkanTextureHandle *textureHandle; + VkComponentMapping swizzle = IDENTITY_SWIZZLE; + + format = SDLToVK_SurfaceFormat[textureCreateInfo->format]; + + /* FIXME: We probably need a swizzle table like FNA3D Vulkan does */ + if (textureCreateInfo->format == REFRESH_TEXTUREFORMAT_A8) + { + swizzle.r = VK_COMPONENT_SWIZZLE_ZERO; + swizzle.g = VK_COMPONENT_SWIZZLE_ZERO; + swizzle.b = VK_COMPONENT_SWIZZLE_ZERO; + swizzle.a = VK_COMPONENT_SWIZZLE_R; + } + + if (isDepthFormat) + { + imageAspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT; + + if (IsStencilFormat(textureCreateInfo->format)) + { + imageAspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } + else + { + imageAspectFlags = VK_IMAGE_ASPECT_COLOR_BIT; + } + + textureHandle = VULKAN_INTERNAL_CreateTextureHandle( + renderer, + textureCreateInfo->width, + textureCreateInfo->height, + textureCreateInfo->depth, + textureCreateInfo->isCube, + textureCreateInfo->layerCount, + textureCreateInfo->levelCount, + SDLToVK_SampleCount[textureCreateInfo->sampleCount], + format, + swizzle, + imageAspectFlags, + textureCreateInfo->usageFlags + ); + + if (textureHandle == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture container!"); + return NULL; + } + + container = SDL_malloc(sizeof(VulkanTextureContainer)); + container->canBeCycled = 1; + container->activeTextureHandle = textureHandle; + container->textureCapacity = 1; + container->textureCount = 1 ; + container->textureHandles = SDL_malloc( + container->textureCapacity * sizeof(VulkanTextureHandle*) + ); + container->textureHandles[0] = container->activeTextureHandle; + container->debugName = NULL; + + textureHandle->container = container; + + return (Refresh_Texture*) container; +} + +static Refresh_Buffer* VULKAN_CreateBuffer( + Refresh_Renderer *driverData, + Refresh_BufferUsageFlags usageFlags, + Uint32 sizeInBytes +) { + return (Refresh_Buffer*) VULKAN_INTERNAL_CreateBufferContainer( + (VulkanRenderer*) driverData, + sizeInBytes, + usageFlags, + VULKAN_BUFFER_TYPE_GPU + ); +} + +static VulkanUniformBuffer* VULKAN_INTERNAL_CreateUniformBuffer( + VulkanRenderer *renderer, + Uint32 sizeInBytes +) { + VulkanUniformBuffer *uniformBuffer = SDL_malloc(sizeof(VulkanUniformBuffer)); + + uniformBuffer->bufferContainer = VULKAN_INTERNAL_CreateBufferContainer( + renderer, + sizeInBytes, + 0, + VULKAN_BUFFER_TYPE_UNIFORM + ); + + uniformBuffer->drawOffset = 0; + uniformBuffer->offset = 0; + + return uniformBuffer; +} + +static Refresh_TransferBuffer* VULKAN_CreateTransferBuffer( + Refresh_Renderer *driverData, + Refresh_TransferUsage usage, /* ignored on Vulkan */ + Refresh_TransferBufferMapFlags mapFlags, /* ignored on Vulkan */ + Uint32 sizeInBytes +) { + return (Refresh_TransferBuffer*) VULKAN_INTERNAL_CreateBufferContainer( + (VulkanRenderer*) driverData, + sizeInBytes, + 0, + VULKAN_BUFFER_TYPE_TRANSFER + ); +} + +static void VULKAN_INTERNAL_ReleaseTexture( + VulkanRenderer *renderer, + VulkanTexture *vulkanTexture +) { + if (vulkanTexture->markedForDestroy) { return; } + + SDL_LockMutex(renderer->disposeLock); + + EXPAND_ARRAY_IF_NEEDED( + renderer->texturesToDestroy, + VulkanTexture*, + renderer->texturesToDestroyCount + 1, + renderer->texturesToDestroyCapacity, + renderer->texturesToDestroyCapacity * 2 + ) + + renderer->texturesToDestroy[ + renderer->texturesToDestroyCount + ] = vulkanTexture; + renderer->texturesToDestroyCount += 1; + + vulkanTexture->markedForDestroy = 1; + + SDL_UnlockMutex(renderer->disposeLock); +} + +static void VULKAN_ReleaseTexture( + Refresh_Renderer *driverData, + Refresh_Texture *texture +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanTextureContainer *vulkanTextureContainer = (VulkanTextureContainer*) texture; + Uint32 i; + + SDL_LockMutex(renderer->disposeLock); + + for (i = 0; i < vulkanTextureContainer->textureCount; i += 1) + { + VULKAN_INTERNAL_ReleaseTexture(renderer, vulkanTextureContainer->textureHandles[i]->vulkanTexture); + SDL_free(vulkanTextureContainer->textureHandles[i]); + } + + /* Containers are just client handles, so we can destroy immediately */ + if (vulkanTextureContainer->debugName != NULL) + { + SDL_free(vulkanTextureContainer->debugName); + } + SDL_free(vulkanTextureContainer->textureHandles); + SDL_free(vulkanTextureContainer); + + SDL_UnlockMutex(renderer->disposeLock); +} + +static void VULKAN_ReleaseSampler( + Refresh_Renderer *driverData, + Refresh_Sampler *sampler +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanSampler* vulkanSampler = (VulkanSampler*) sampler; + + SDL_LockMutex(renderer->disposeLock); + + EXPAND_ARRAY_IF_NEEDED( + renderer->samplersToDestroy, + VulkanSampler*, + renderer->samplersToDestroyCount + 1, + renderer->samplersToDestroyCapacity, + renderer->samplersToDestroyCapacity * 2 + ) + + renderer->samplersToDestroy[renderer->samplersToDestroyCount] = vulkanSampler; + renderer->samplersToDestroyCount += 1; + + SDL_UnlockMutex(renderer->disposeLock); +} + +static void VULKAN_INTERNAL_ReleaseBuffer( + VulkanRenderer *renderer, + VulkanBuffer *vulkanBuffer +) { + if (vulkanBuffer->markedForDestroy) { return; } + + SDL_LockMutex(renderer->disposeLock); + + EXPAND_ARRAY_IF_NEEDED( + renderer->buffersToDestroy, + VulkanBuffer*, + renderer->buffersToDestroyCount + 1, + renderer->buffersToDestroyCapacity, + renderer->buffersToDestroyCapacity * 2 + ) + + renderer->buffersToDestroy[ + renderer->buffersToDestroyCount + ] = vulkanBuffer; + renderer->buffersToDestroyCount += 1; + + vulkanBuffer->markedForDestroy = 1; + + SDL_UnlockMutex(renderer->disposeLock); +} + +static void VULKAN_INTERNAL_ReleaseBufferContainer( + VulkanRenderer *renderer, + VulkanBufferContainer *bufferContainer +) { + Uint32 i; + + SDL_LockMutex(renderer->disposeLock); + + for (i = 0; i < bufferContainer->bufferCount; i += 1) + { + VULKAN_INTERNAL_ReleaseBuffer(renderer, bufferContainer->bufferHandles[i]->vulkanBuffer); + SDL_free(bufferContainer->bufferHandles[i]); + } + + /* Containers are just client handles, so we can free immediately */ + if (bufferContainer->debugName != NULL) + { + SDL_free(bufferContainer->debugName); + } + SDL_free(bufferContainer->bufferHandles); + SDL_free(bufferContainer); + + SDL_UnlockMutex(renderer->disposeLock); +} + +static void VULKAN_ReleaseBuffer( + Refresh_Renderer *driverData, + Refresh_Buffer *buffer +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanBufferContainer *vulkanBufferContainer = (VulkanBufferContainer*) buffer; + + VULKAN_INTERNAL_ReleaseBufferContainer( + renderer, + vulkanBufferContainer + ); +} + +static void VULKAN_ReleaseTransferBuffer( + Refresh_Renderer *driverData, + Refresh_TransferBuffer *transferBuffer +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; + + VULKAN_INTERNAL_ReleaseBufferContainer( + renderer, + transferBufferContainer + ); +} + +static void VULKAN_ReleaseShader( + Refresh_Renderer *driverData, + Refresh_Shader *shader +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanShader *vulkanShader = (VulkanShader*) shader; + + SDL_LockMutex(renderer->disposeLock); + + EXPAND_ARRAY_IF_NEEDED( + renderer->shadersToDestroy, + VulkanShader*, + renderer->shadersToDestroyCount + 1, + renderer->shadersToDestroyCapacity, + renderer->shadersToDestroyCapacity * 2 + ) + + renderer->shadersToDestroy[renderer->shadersToDestroyCount] = vulkanShader; + renderer->shadersToDestroyCount += 1; + + SDL_UnlockMutex(renderer->disposeLock); +} + +static void VULKAN_ReleaseComputePipeline( + Refresh_Renderer *driverData, + Refresh_ComputePipeline *computePipeline +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanComputePipeline *vulkanComputePipeline = (VulkanComputePipeline*) computePipeline; + + SDL_LockMutex(renderer->disposeLock); + + EXPAND_ARRAY_IF_NEEDED( + renderer->computePipelinesToDestroy, + VulkanComputePipeline*, + renderer->computePipelinesToDestroyCount + 1, + renderer->computePipelinesToDestroyCapacity, + renderer->computePipelinesToDestroyCapacity * 2 + ) + + renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount] = vulkanComputePipeline; + renderer->computePipelinesToDestroyCount += 1; + + SDL_UnlockMutex(renderer->disposeLock); +} + +static void VULKAN_ReleaseGraphicsPipeline( + Refresh_Renderer *driverData, + Refresh_GraphicsPipeline *graphicsPipeline +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanGraphicsPipeline *vulkanGraphicsPipeline = (VulkanGraphicsPipeline*) graphicsPipeline; + + SDL_LockMutex(renderer->disposeLock); + + EXPAND_ARRAY_IF_NEEDED( + renderer->graphicsPipelinesToDestroy, + VulkanGraphicsPipeline*, + renderer->graphicsPipelinesToDestroyCount + 1, + renderer->graphicsPipelinesToDestroyCapacity, + renderer->graphicsPipelinesToDestroyCapacity * 2 + ) + + renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount] = vulkanGraphicsPipeline; + renderer->graphicsPipelinesToDestroyCount += 1; + + SDL_UnlockMutex(renderer->disposeLock); +} + +static void VULKAN_ReleaseOcclusionQuery( + Refresh_Renderer *driverData, + Refresh_OcclusionQuery *query +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanOcclusionQuery *vulkanQuery = (VulkanOcclusionQuery*) query; + + SDL_LockMutex(renderer->queryLock); + + /* Push the now-free index to the stack */ + renderer->freeQueryIndexStack[vulkanQuery->index] = + renderer->freeQueryIndexStackHead; + renderer->freeQueryIndexStackHead = vulkanQuery->index; + + SDL_UnlockMutex(renderer->queryLock); +} + +/* Command Buffer render state */ + +static VkRenderPass VULKAN_INTERNAL_FetchRenderPass( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + Refresh_ColorAttachmentInfo *colorAttachmentInfos, + Uint32 colorAttachmentCount, + Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo +) { + VkRenderPass renderPass; + RenderPassHash hash; + Uint32 i; + + SDL_LockMutex(renderer->renderPassFetchLock); + + for (i = 0; i < colorAttachmentCount; i += 1) + { + hash.colorTargetDescriptions[i].format = ((VulkanTextureContainer*) colorAttachmentInfos[i].textureSlice.texture)->activeTextureHandle->vulkanTexture->format; + hash.colorTargetDescriptions[i].clearColor = colorAttachmentInfos[i].clearColor; + hash.colorTargetDescriptions[i].loadOp = colorAttachmentInfos[i].loadOp; + hash.colorTargetDescriptions[i].storeOp = colorAttachmentInfos[i].storeOp; + } + + hash.colorAttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT; + if (colorAttachmentCount > 0) + { + hash.colorAttachmentSampleCount = ((VulkanTextureContainer*) colorAttachmentInfos[0].textureSlice.texture)->activeTextureHandle->vulkanTexture->sampleCount; + } + + hash.colorAttachmentCount = colorAttachmentCount; + + if (depthStencilAttachmentInfo == NULL) + { + hash.depthStencilTargetDescription.format = 0; + hash.depthStencilTargetDescription.loadOp = REFRESH_LOADOP_DONT_CARE; + hash.depthStencilTargetDescription.storeOp = REFRESH_STOREOP_DONT_CARE; + hash.depthStencilTargetDescription.stencilLoadOp = REFRESH_LOADOP_DONT_CARE; + hash.depthStencilTargetDescription.stencilStoreOp = REFRESH_STOREOP_DONT_CARE; + } + else + { + hash.depthStencilTargetDescription.format = ((VulkanTextureContainer*) depthStencilAttachmentInfo->textureSlice.texture)->activeTextureHandle->vulkanTexture->format; + hash.depthStencilTargetDescription.loadOp = depthStencilAttachmentInfo->loadOp; + hash.depthStencilTargetDescription.storeOp = depthStencilAttachmentInfo->storeOp; + hash.depthStencilTargetDescription.stencilLoadOp = depthStencilAttachmentInfo->stencilLoadOp; + hash.depthStencilTargetDescription.stencilStoreOp = depthStencilAttachmentInfo->stencilStoreOp; + } + + renderPass = RenderPassHashArray_Fetch( + &renderer->renderPassHashArray, + &hash + ); + + if (renderPass != VK_NULL_HANDLE) + { + SDL_UnlockMutex(renderer->renderPassFetchLock); + return renderPass; + } + + renderPass = VULKAN_INTERNAL_CreateRenderPass( + renderer, + commandBuffer, + colorAttachmentInfos, + colorAttachmentCount, + depthStencilAttachmentInfo + ); + + if (renderPass != VK_NULL_HANDLE) + { + RenderPassHashArray_Insert( + &renderer->renderPassHashArray, + hash, + renderPass + ); + } + + SDL_UnlockMutex(renderer->renderPassFetchLock); + return renderPass; +} + +static VulkanFramebuffer* VULKAN_INTERNAL_FetchFramebuffer( + VulkanRenderer *renderer, + VkRenderPass renderPass, + Refresh_ColorAttachmentInfo *colorAttachmentInfos, + Uint32 colorAttachmentCount, + Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo, + Uint32 width, + Uint32 height +) { + VulkanFramebuffer *vulkanFramebuffer; + VkFramebufferCreateInfo framebufferInfo; + VkResult result; + VkImageView imageViewAttachments[2 * MAX_COLOR_TARGET_BINDINGS + 1]; + FramebufferHash hash; + VulkanTextureSlice *textureSlice; + Uint32 attachmentCount = 0; + Uint32 i; + + for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) + { + hash.colorAttachmentViews[i] = VK_NULL_HANDLE; + hash.colorMultiSampleAttachmentViews[i] = VK_NULL_HANDLE; + } + + hash.colorAttachmentCount = colorAttachmentCount; + + for (i = 0; i < colorAttachmentCount; i += 1) + { + textureSlice = VULKAN_INTERNAL_SDLToVulkanTextureSlice(&colorAttachmentInfos[i].textureSlice); + + hash.colorAttachmentViews[i] = textureSlice->view; + + if (textureSlice->msaaTexHandle != NULL) + { + hash.colorMultiSampleAttachmentViews[i] = textureSlice->msaaTexHandle->vulkanTexture->view; + } + } + + if (depthStencilAttachmentInfo == NULL) + { + hash.depthStencilAttachmentView = VK_NULL_HANDLE; + } + else + { + textureSlice = VULKAN_INTERNAL_SDLToVulkanTextureSlice(&depthStencilAttachmentInfo->textureSlice); + hash.depthStencilAttachmentView = textureSlice->view; + } + + hash.width = width; + hash.height = height; + + SDL_LockMutex(renderer->framebufferFetchLock); + + vulkanFramebuffer = FramebufferHashArray_Fetch( + &renderer->framebufferHashArray, + &hash + ); + + SDL_UnlockMutex(renderer->framebufferFetchLock); + + if (vulkanFramebuffer != NULL) + { + return vulkanFramebuffer; + } + + vulkanFramebuffer = SDL_malloc(sizeof(VulkanFramebuffer)); + + SDL_AtomicSet(&vulkanFramebuffer->referenceCount, 0); + + /* Create a new framebuffer */ + + for (i = 0; i < colorAttachmentCount; i += 1) + { + textureSlice = VULKAN_INTERNAL_SDLToVulkanTextureSlice(&colorAttachmentInfos[i].textureSlice); + + imageViewAttachments[attachmentCount] = + textureSlice->view; + + attachmentCount += 1; + + if (textureSlice->msaaTexHandle != NULL) + { + imageViewAttachments[attachmentCount] = + textureSlice->msaaTexHandle->vulkanTexture->view; + + attachmentCount += 1; + } + } + + if (depthStencilAttachmentInfo != NULL) + { + textureSlice = VULKAN_INTERNAL_SDLToVulkanTextureSlice(&depthStencilAttachmentInfo->textureSlice); + + imageViewAttachments[attachmentCount] = + textureSlice->view; + + attachmentCount += 1; + } + + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.pNext = NULL; + framebufferInfo.flags = 0; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = attachmentCount; + framebufferInfo.pAttachments = imageViewAttachments; + framebufferInfo.width = hash.width; + framebufferInfo.height = hash.height; + framebufferInfo.layers = 1; + + result = renderer->vkCreateFramebuffer( + renderer->logicalDevice, + &framebufferInfo, + NULL, + &vulkanFramebuffer->framebuffer + ); + + if (result == VK_SUCCESS) + { + SDL_LockMutex(renderer->framebufferFetchLock); + + FramebufferHashArray_Insert( + &renderer->framebufferHashArray, + hash, + vulkanFramebuffer + ); + + SDL_UnlockMutex(renderer->framebufferFetchLock); + } + else + { + LogVulkanResultAsError("vkCreateFramebuffer", result); + SDL_free(vulkanFramebuffer); + vulkanFramebuffer = NULL; + } + + return vulkanFramebuffer; +} + +static void VULKAN_INTERNAL_SetCurrentViewport( + VulkanCommandBuffer *commandBuffer, + Refresh_Viewport *viewport +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + + vulkanCommandBuffer->currentViewport.x = viewport->x; + vulkanCommandBuffer->currentViewport.width = viewport->w; + vulkanCommandBuffer->currentViewport.minDepth = viewport->minDepth; + vulkanCommandBuffer->currentViewport.maxDepth = viewport->maxDepth; + + /* Viewport flip for consistency with other backends */ + /* FIXME: need moltenVK hack */ + vulkanCommandBuffer->currentViewport.y = viewport->y + viewport->h; + vulkanCommandBuffer->currentViewport.height = -viewport->h; +} + +static void VULKAN_SetViewport( + Refresh_CommandBuffer *commandBuffer, + Refresh_Viewport *viewport +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + + VULKAN_INTERNAL_SetCurrentViewport( + vulkanCommandBuffer, + viewport + ); + + renderer->vkCmdSetViewport( + vulkanCommandBuffer->commandBuffer, + 0, + 1, + &vulkanCommandBuffer->currentViewport + ); +} + +static void VULKAN_INTERNAL_SetCurrentScissor( + VulkanCommandBuffer *vulkanCommandBuffer, + Refresh_Rect *scissor +) { + vulkanCommandBuffer->currentScissor.offset.x = scissor->x; + vulkanCommandBuffer->currentScissor.offset.y = scissor->y; + vulkanCommandBuffer->currentScissor.extent.width = scissor->w; + vulkanCommandBuffer->currentScissor.extent.height = scissor->h; +} + +static void VULKAN_SetScissor( + Refresh_CommandBuffer *commandBuffer, + Refresh_Rect *scissor +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + + VULKAN_INTERNAL_SetCurrentScissor( + vulkanCommandBuffer, + scissor + ); + + renderer->vkCmdSetScissor( + vulkanCommandBuffer->commandBuffer, + 0, + 1, + &vulkanCommandBuffer->currentScissor + ); +} + +static void VULKAN_BindVertexSamplers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSamplerBinding *textureSamplerBindings, + Uint32 bindingCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + VulkanTextureContainer *textureContainer; + Uint32 i, j; + + for (i = 0; i < bindingCount; i += 1) + { + textureContainer = (VulkanTextureContainer*) textureSamplerBindings[i].texture; + vulkanCommandBuffer->vertexSamplerTextures[firstSlot + i] = textureContainer->activeTextureHandle->vulkanTexture; + vulkanCommandBuffer->vertexSamplers[firstSlot + i] = (VulkanSampler*) textureSamplerBindings[i].sampler; + + VULKAN_INTERNAL_TrackSampler( + renderer, + vulkanCommandBuffer, + (VulkanSampler*) textureSamplerBindings[i].sampler + ); + + for (j = 0; j < textureContainer->activeTextureHandle->vulkanTexture->sliceCount; j += 1) + { + VULKAN_INTERNAL_TrackTextureSlice( + renderer, + vulkanCommandBuffer, + &textureContainer->activeTextureHandle->vulkanTexture->slices[j] + ); + } + } + + vulkanCommandBuffer->needNewVertexResourceDescriptorSet = SDL_TRUE; +} + +static void VULKAN_BindVertexStorageTextures( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + VulkanTextureContainer *textureContainer; + VulkanTextureSlice *textureSlice; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + textureContainer = (VulkanTextureContainer*) storageTextureSlices[i].texture; + textureSlice = VULKAN_INTERNAL_FetchTextureSlice( + textureContainer->activeTextureHandle->vulkanTexture, + storageTextureSlices[i].layer, + storageTextureSlices[i].mipLevel + ); + + vulkanCommandBuffer->vertexStorageTextureSlices[firstSlot + i] = textureSlice; + + VULKAN_INTERNAL_TrackTextureSlice( + renderer, + vulkanCommandBuffer, + textureSlice + ); + } + + vulkanCommandBuffer->needNewVertexResourceDescriptorSet = SDL_TRUE; +} + +static void VULKAN_BindVertexStorageBuffers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + VulkanBufferContainer *bufferContainer; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + bufferContainer = (VulkanBufferContainer*) storageBuffers[i]; + + vulkanCommandBuffer->vertexStorageBuffers[firstSlot + i] = bufferContainer->activeBufferHandle->vulkanBuffer; + + VULKAN_INTERNAL_TrackBuffer( + renderer, + vulkanCommandBuffer, + bufferContainer->activeBufferHandle->vulkanBuffer + ); + } + + vulkanCommandBuffer->needNewVertexResourceDescriptorSet = SDL_TRUE; +} + +static void VULKAN_BindFragmentSamplers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSamplerBinding *textureSamplerBindings, + Uint32 bindingCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + VulkanTextureContainer *textureContainer; + Uint32 i, j; + + for (i = 0; i < bindingCount; i += 1) + { + textureContainer = (VulkanTextureContainer*) textureSamplerBindings[i].texture; + vulkanCommandBuffer->fragmentSamplerTextures[firstSlot + i] = textureContainer->activeTextureHandle->vulkanTexture; + vulkanCommandBuffer->fragmentSamplers[firstSlot + i] = (VulkanSampler*) textureSamplerBindings[i].sampler; + + VULKAN_INTERNAL_TrackSampler( + renderer, + vulkanCommandBuffer, + (VulkanSampler*) textureSamplerBindings[i].sampler + ); + + for (j = 0; j < textureContainer->activeTextureHandle->vulkanTexture->sliceCount; j += 1) + { + VULKAN_INTERNAL_TrackTextureSlice( + renderer, + vulkanCommandBuffer, + &textureContainer->activeTextureHandle->vulkanTexture->slices[j] + ); + } + } + + vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = SDL_TRUE; +} + +static void VULKAN_BindFragmentStorageTextures( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + VulkanTextureContainer *textureContainer; + VulkanTextureSlice *textureSlice; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + textureContainer = (VulkanTextureContainer*) storageTextureSlices[i].texture; + textureSlice = VULKAN_INTERNAL_FetchTextureSlice( + textureContainer->activeTextureHandle->vulkanTexture, + storageTextureSlices[i].layer, + storageTextureSlices[i].mipLevel + ); + + vulkanCommandBuffer->fragmentStorageTextureSlices[firstSlot + i] = textureSlice; + + VULKAN_INTERNAL_TrackTextureSlice( + renderer, + vulkanCommandBuffer, + textureSlice + ); + } + + vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = SDL_TRUE; +} + +static void VULKAN_BindFragmentStorageBuffers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + VulkanBufferContainer *bufferContainer; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + bufferContainer = (VulkanBufferContainer*) storageBuffers[i]; + + vulkanCommandBuffer->fragmentStorageBuffers[firstSlot + i] = bufferContainer->activeBufferHandle->vulkanBuffer; + + VULKAN_INTERNAL_TrackBuffer( + renderer, + vulkanCommandBuffer, + bufferContainer->activeBufferHandle->vulkanBuffer + ); + } + + vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = SDL_TRUE; +} + +static void VULKAN_INTERNAL_PushUniformData( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer, + VulkanUniformBuffer *uniformBuffer, + Refresh_ShaderStage shaderStage, + void *data, + Uint32 dataLengthInBytes +) { + Uint32 blockSize = + VULKAN_INTERNAL_NextHighestAlignment32( + dataLengthInBytes, + renderer->minUBOAlignment + ); + + /* If there is no more room, cycle the uniform buffer */ + if (uniformBuffer->offset + blockSize + MAX_UBO_SECTION_SIZE >= uniformBuffer->bufferContainer->activeBufferHandle->vulkanBuffer->size) + { + VULKAN_INTERNAL_CycleActiveBuffer( + renderer, + uniformBuffer->bufferContainer + ); + + uniformBuffer->drawOffset = 0; + uniformBuffer->offset = 0; + + VULKAN_INTERNAL_TrackBuffer( + renderer, + commandBuffer, + uniformBuffer->bufferContainer->activeBufferHandle->vulkanBuffer + ); + + if (shaderStage == REFRESH_SHADERSTAGE_VERTEX) + { + commandBuffer->needNewVertexUniformDescriptorSet = SDL_TRUE; + } + else if (shaderStage == REFRESH_SHADERSTAGE_FRAGMENT) + { + commandBuffer->needNewFragmentUniformDescriptorSet = SDL_TRUE; + } + else if (shaderStage == REFRESH_SHADERSTAGE_COMPUTE) + { + commandBuffer->needNewComputeUniformDescriptorSet = SDL_TRUE; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized shader stage!"); + return; + } + } + + uniformBuffer->drawOffset = uniformBuffer->offset; + + Uint8 *dst = + uniformBuffer->bufferContainer->activeBufferHandle->vulkanBuffer->usedRegion->allocation->mapPointer + + uniformBuffer->bufferContainer->activeBufferHandle->vulkanBuffer->usedRegion->resourceOffset + + uniformBuffer->offset; + + SDL_memcpy( + dst, + data, + dataLengthInBytes + ); + + uniformBuffer->offset += blockSize; + + if (shaderStage == REFRESH_SHADERSTAGE_VERTEX) + { + commandBuffer->needNewVertexUniformOffsets = SDL_TRUE; + } + else if (shaderStage == REFRESH_SHADERSTAGE_FRAGMENT) + { + commandBuffer->needNewFragmentUniformOffsets = SDL_TRUE; + } + else if (shaderStage == REFRESH_SHADERSTAGE_COMPUTE) + { + commandBuffer->needNewComputeUniformOffsets = SDL_TRUE; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized shader stage!"); + return; + } +} + +static void VULKAN_BeginRenderPass( + Refresh_CommandBuffer *commandBuffer, + Refresh_ColorAttachmentInfo *colorAttachmentInfos, + Uint32 colorAttachmentCount, + Refresh_DepthStencilAttachmentInfo *depthStencilAttachmentInfo +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VkRenderPass renderPass; + VulkanFramebuffer *framebuffer; + + VulkanTextureContainer *textureContainer; + VulkanTextureSlice *textureSlice; + Uint32 w, h; + VkClearValue *clearValues; + Uint32 clearCount = colorAttachmentCount; + Uint32 multisampleAttachmentCount = 0; + Uint32 totalColorAttachmentCount = 0; + Uint32 i; + Refresh_Viewport defaultViewport; + Refresh_Rect defaultScissor; + Uint32 framebufferWidth = UINT32_MAX; + Uint32 framebufferHeight = UINT32_MAX; + + for (i = 0; i < colorAttachmentCount; i += 1) + { + textureContainer = (VulkanTextureContainer*) colorAttachmentInfos[i].textureSlice.texture; + + w = textureContainer->activeTextureHandle->vulkanTexture->dimensions.width >> colorAttachmentInfos[i].textureSlice.mipLevel; + h = textureContainer->activeTextureHandle->vulkanTexture->dimensions.height >> colorAttachmentInfos[i].textureSlice.mipLevel; + + /* The framebuffer cannot be larger than the smallest attachment. */ + + if (w < framebufferWidth) + { + framebufferWidth = w; + } + + if (h < framebufferHeight) + { + framebufferHeight = h; + } + + if (!textureContainer->activeTextureHandle->vulkanTexture->isRenderTarget) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Color attachment texture was not designated as a target!"); + return; + } + } + + if (depthStencilAttachmentInfo != NULL) + { + textureContainer = (VulkanTextureContainer*) depthStencilAttachmentInfo->textureSlice.texture; + + w = textureContainer->activeTextureHandle->vulkanTexture->dimensions.width >> depthStencilAttachmentInfo->textureSlice.mipLevel; + h = textureContainer->activeTextureHandle->vulkanTexture->dimensions.height >> depthStencilAttachmentInfo->textureSlice.mipLevel; + + /* The framebuffer cannot be larger than the smallest attachment. */ + + if (w < framebufferWidth) + { + framebufferWidth = w; + } + + if (h < framebufferHeight) + { + framebufferHeight = h; + } + + if (!textureContainer->activeTextureHandle->vulkanTexture->isRenderTarget) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Depth stencil attachment texture was not designated as a target!"); + return; + } + } + + /* Layout transitions */ + + for (i = 0; i < colorAttachmentCount; i += 1) + { + SDL_bool cycle; + if (colorAttachmentInfos[i].loadOp) + { + cycle = SDL_FALSE; + } + else + { + cycle = colorAttachmentInfos[i].cycle; + } + + textureContainer = (VulkanTextureContainer*) colorAttachmentInfos[i].textureSlice.texture; + textureSlice = VULKAN_INTERNAL_PrepareTextureSliceForWrite( + renderer, + vulkanCommandBuffer, + textureContainer, + colorAttachmentInfos[i].textureSlice.layer, + colorAttachmentInfos[i].textureSlice.mipLevel, + cycle, + VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT + ); + + if (textureSlice->msaaTexHandle != NULL) + { + /* Transition the multisample attachment */ + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT, + &textureSlice->msaaTexHandle->vulkanTexture->slices[0] + ); + + clearCount += 1; + multisampleAttachmentCount += 1; + } + + vulkanCommandBuffer->colorAttachmentSlices[i] = textureSlice; + + VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, textureSlice); + /* TODO: do we need to track the msaa texture? or is it implicitly only used when the regular texture is used? */ + } + + vulkanCommandBuffer->colorAttachmentSliceCount = colorAttachmentCount; + + if (depthStencilAttachmentInfo != NULL) + { + SDL_bool cycle; + + if ( + depthStencilAttachmentInfo->loadOp == REFRESH_LOADOP_LOAD || + depthStencilAttachmentInfo->stencilLoadOp == REFRESH_LOADOP_LOAD + ) { + cycle = SDL_FALSE; + } + else + { + cycle = depthStencilAttachmentInfo->cycle; + } + + textureContainer = (VulkanTextureContainer*) depthStencilAttachmentInfo->textureSlice.texture; + textureSlice = VULKAN_INTERNAL_PrepareTextureSliceForWrite( + renderer, + vulkanCommandBuffer, + textureContainer, + depthStencilAttachmentInfo->textureSlice.layer, + depthStencilAttachmentInfo->textureSlice.mipLevel, + cycle, + VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT + ); + + clearCount += 1; + + vulkanCommandBuffer->depthStencilAttachmentSlice = textureSlice; + + VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, textureSlice); + } + + /* Fetch required render objects */ + + renderPass = VULKAN_INTERNAL_FetchRenderPass( + renderer, + vulkanCommandBuffer, + colorAttachmentInfos, + colorAttachmentCount, + depthStencilAttachmentInfo + ); + + framebuffer = VULKAN_INTERNAL_FetchFramebuffer( + renderer, + renderPass, + colorAttachmentInfos, + colorAttachmentCount, + depthStencilAttachmentInfo, + framebufferWidth, + framebufferHeight + ); + + VULKAN_INTERNAL_TrackFramebuffer(renderer, vulkanCommandBuffer, framebuffer); + + /* Set clear values */ + + clearValues = SDL_stack_alloc(VkClearValue, clearCount); + + totalColorAttachmentCount = colorAttachmentCount + multisampleAttachmentCount; + + for (i = 0; i < totalColorAttachmentCount; i += 1) + { + clearValues[i].color.float32[0] = colorAttachmentInfos[i].clearColor.r; + clearValues[i].color.float32[1] = colorAttachmentInfos[i].clearColor.g; + clearValues[i].color.float32[2] = colorAttachmentInfos[i].clearColor.b; + clearValues[i].color.float32[3] = colorAttachmentInfos[i].clearColor.a; + + textureSlice = VULKAN_INTERNAL_SDLToVulkanTextureSlice(&colorAttachmentInfos[i].textureSlice); + + if (textureSlice->parent->sampleCount > VK_SAMPLE_COUNT_1_BIT) + { + clearValues[i+1].color.float32[0] = colorAttachmentInfos[i].clearColor.r; + clearValues[i+1].color.float32[1] = colorAttachmentInfos[i].clearColor.g; + clearValues[i+1].color.float32[2] = colorAttachmentInfos[i].clearColor.b; + clearValues[i+1].color.float32[3] = colorAttachmentInfos[i].clearColor.a; + i += 1; + } + } + + if (depthStencilAttachmentInfo != NULL) + { + clearValues[totalColorAttachmentCount].depthStencil.depth = + depthStencilAttachmentInfo->depthStencilClearValue.depth; + clearValues[totalColorAttachmentCount].depthStencil.stencil = + depthStencilAttachmentInfo->depthStencilClearValue.stencil; + } + + VkRenderPassBeginInfo renderPassBeginInfo; + renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassBeginInfo.pNext = NULL; + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.framebuffer = framebuffer->framebuffer; + renderPassBeginInfo.pClearValues = clearValues; + renderPassBeginInfo.clearValueCount = clearCount; + renderPassBeginInfo.renderArea.extent.width = framebufferWidth; + renderPassBeginInfo.renderArea.extent.height = framebufferHeight; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + + renderer->vkCmdBeginRenderPass( + vulkanCommandBuffer->commandBuffer, + &renderPassBeginInfo, + VK_SUBPASS_CONTENTS_INLINE + ); + + SDL_stack_free(clearValues); + + /* Set sensible default viewport state */ + + defaultViewport.x = 0; + defaultViewport.y = 0; + defaultViewport.w = framebufferWidth; + defaultViewport.h = framebufferHeight; + defaultViewport.minDepth = 0; + defaultViewport.maxDepth = 1; + + VULKAN_INTERNAL_SetCurrentViewport( + vulkanCommandBuffer, + &defaultViewport + ); + + defaultScissor.x = 0; + defaultScissor.y = 0; + defaultScissor.w = framebufferWidth; + defaultScissor.h = framebufferHeight; + + VULKAN_INTERNAL_SetCurrentScissor( + vulkanCommandBuffer, + &defaultScissor + ); +} + +static void VULKAN_BindGraphicsPipeline( + Refresh_CommandBuffer *commandBuffer, + Refresh_GraphicsPipeline *graphicsPipeline +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanGraphicsPipeline* pipeline = (VulkanGraphicsPipeline*) graphicsPipeline; + Uint32 i; + + renderer->vkCmdBindPipeline( + vulkanCommandBuffer->commandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + pipeline->pipeline + ); + + vulkanCommandBuffer->currentGraphicsPipeline = pipeline; + + VULKAN_INTERNAL_TrackGraphicsPipeline(renderer, vulkanCommandBuffer, pipeline); + + renderer->vkCmdSetViewport( + vulkanCommandBuffer->commandBuffer, + 0, + 1, + &vulkanCommandBuffer->currentViewport + ); + + renderer->vkCmdSetScissor( + vulkanCommandBuffer->commandBuffer, + 0, + 1, + &vulkanCommandBuffer->currentScissor + ); + + for (i = vulkanCommandBuffer->initializedVertexUniformBufferCount; i < pipeline->resourceLayout.vertexUniformBufferCount; i += 1) + { + vulkanCommandBuffer->vertexUniformBuffers[i] = VULKAN_INTERNAL_CreateUniformBuffer(renderer, UNIFORM_BUFFER_SIZE); + vulkanCommandBuffer->initializedVertexUniformBufferCount += 1; + } + + for (i = vulkanCommandBuffer->initializedFragmentUniformBufferCount; i < pipeline->resourceLayout.fragmentUniformBufferCount; i += 1) + { + vulkanCommandBuffer->fragmentUniformBuffers[i] = VULKAN_INTERNAL_CreateUniformBuffer(renderer, UNIFORM_BUFFER_SIZE); + vulkanCommandBuffer->initializedFragmentUniformBufferCount += 1; + } + + for (i = 0; i < pipeline->resourceLayout.vertexUniformBufferCount; i += 1) + { + VULKAN_INTERNAL_TrackBuffer( + renderer, + vulkanCommandBuffer, + vulkanCommandBuffer->vertexUniformBuffers[i]->bufferContainer->activeBufferHandle->vulkanBuffer + ); + } + + for (i = 0; i < pipeline->resourceLayout.fragmentUniformBufferCount; i += 1) + { + VULKAN_INTERNAL_TrackBuffer( + renderer, + vulkanCommandBuffer, + vulkanCommandBuffer->fragmentUniformBuffers[i]->bufferContainer->activeBufferHandle->vulkanBuffer + ); + } + + vulkanCommandBuffer->needNewVertexResourceDescriptorSet = SDL_TRUE; + vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = SDL_TRUE; + vulkanCommandBuffer->needNewVertexUniformDescriptorSet = SDL_TRUE; + vulkanCommandBuffer->needNewFragmentUniformDescriptorSet = SDL_TRUE; + vulkanCommandBuffer->needNewVertexUniformOffsets = SDL_TRUE; + vulkanCommandBuffer->needNewFragmentUniformOffsets = SDL_TRUE; +} + +static void VULKAN_BindVertexBuffers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstBinding, + Refresh_BufferBinding *pBindings, + Uint32 bindingCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanBuffer *currentVulkanBuffer; + VkBuffer *buffers = SDL_stack_alloc(VkBuffer, bindingCount); + VkDeviceSize *offsets = SDL_stack_alloc(VkDeviceSize, bindingCount); + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + currentVulkanBuffer = ((VulkanBufferContainer*) pBindings[i].buffer)->activeBufferHandle->vulkanBuffer; + buffers[i] = currentVulkanBuffer->buffer; + offsets[i] = (VkDeviceSize) pBindings[i].offset; + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, currentVulkanBuffer); + } + + renderer->vkCmdBindVertexBuffers( + vulkanCommandBuffer->commandBuffer, + firstBinding, + bindingCount, + buffers, + offsets + ); + + SDL_stack_free(buffers); + SDL_stack_free(offsets); +} + +static void VULKAN_BindIndexBuffer( + Refresh_CommandBuffer *commandBuffer, + Refresh_BufferBinding *pBinding, + Refresh_IndexElementSize indexElementSize +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanBuffer* vulkanBuffer = ((VulkanBufferContainer*) pBinding->buffer)->activeBufferHandle->vulkanBuffer; + + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, vulkanBuffer); + + renderer->vkCmdBindIndexBuffer( + vulkanCommandBuffer->commandBuffer, + vulkanBuffer->buffer, + (VkDeviceSize) pBinding->offset, + SDLToVK_IndexType[indexElementSize] + ); +} + +static void VULKAN_PushVertexUniformData( + Refresh_CommandBuffer *commandBuffer, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + + if (slotIndex >= vulkanCommandBuffer->currentGraphicsPipeline->resourceLayout.vertexUniformBufferCount) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No vertex uniforms exist on slot %i for this pipeline", slotIndex); + return; + } + + VULKAN_INTERNAL_PushUniformData( + vulkanCommandBuffer->renderer, + vulkanCommandBuffer, + vulkanCommandBuffer->vertexUniformBuffers[slotIndex], + REFRESH_SHADERSTAGE_VERTEX, + data, + dataLengthInBytes + ); +} + +static void VULKAN_PushFragmentUniformData( + Refresh_CommandBuffer *commandBuffer, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + + if (slotIndex >= vulkanCommandBuffer->currentGraphicsPipeline->resourceLayout.fragmentUniformBufferCount) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No fragment uniforms exist on slot %i for this pipeline", slotIndex); + return; + } + + VULKAN_INTERNAL_PushUniformData( + vulkanCommandBuffer->renderer, + vulkanCommandBuffer, + vulkanCommandBuffer->fragmentUniformBuffers[slotIndex], + REFRESH_SHADERSTAGE_FRAGMENT, + data, + dataLengthInBytes + ); +} + +static void VULKAN_EndRenderPass( + Refresh_CommandBuffer *commandBuffer +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + Uint32 i; + + renderer->vkCmdEndRenderPass( + vulkanCommandBuffer->commandBuffer + ); + + for (i = 0; i < vulkanCommandBuffer->colorAttachmentSliceCount; i += 1) + { + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT, + vulkanCommandBuffer->colorAttachmentSlices[i] + ); + } + vulkanCommandBuffer->colorAttachmentSliceCount = 0; + + if (vulkanCommandBuffer->depthStencilAttachmentSlice != NULL) + { + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT, + vulkanCommandBuffer->depthStencilAttachmentSlice + ); + vulkanCommandBuffer->depthStencilAttachmentSlice = NULL; + } + + vulkanCommandBuffer->currentGraphicsPipeline = NULL; + + vulkanCommandBuffer->vertexResourceDescriptorSet = VK_NULL_HANDLE; + vulkanCommandBuffer->vertexUniformDescriptorSet = VK_NULL_HANDLE; + vulkanCommandBuffer->fragmentResourceDescriptorSet = VK_NULL_HANDLE; + vulkanCommandBuffer->fragmentUniformDescriptorSet = VK_NULL_HANDLE; +} + +static void VULKAN_BeginComputePass( + Refresh_CommandBuffer *commandBuffer, + Refresh_StorageTextureReadWriteBinding *storageTextureBindings, + Uint32 storageTextureBindingCount, + Refresh_StorageBufferReadWriteBinding *storageBufferBindings, + Uint32 storageBufferBindingCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + VulkanTextureContainer *textureContainer; + VulkanTextureSlice *textureSlice; + VulkanBufferContainer *bufferContainer; + VulkanBuffer *buffer; + Uint32 i; + + for (i = 0; i < storageTextureBindingCount; i += 1) + { + textureContainer = (VulkanTextureContainer*) storageTextureBindings[i].textureSlice.texture; + textureSlice = VULKAN_INTERNAL_PrepareTextureSliceForWrite( + renderer, + vulkanCommandBuffer, + textureContainer, + storageTextureBindings[i].textureSlice.layer, + storageTextureBindings[i].textureSlice.mipLevel, + storageTextureBindings[i].cycle, + VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE + ); + + vulkanCommandBuffer->readWriteComputeStorageTextureSlices[i] = textureSlice; + + VULKAN_INTERNAL_TrackTextureSlice( + renderer, + vulkanCommandBuffer, + textureSlice + ); + } + + for (i = 0; i < storageBufferBindingCount; i += 1) + { + bufferContainer = (VulkanBufferContainer*) storageBufferBindings[i].buffer; + buffer = VULKAN_INTERNAL_PrepareBufferForWrite( + renderer, + vulkanCommandBuffer, + bufferContainer, + storageBufferBindings[i].cycle, + VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ + ); + + vulkanCommandBuffer->readWriteComputeStorageBuffers[i] = buffer; + + VULKAN_INTERNAL_TrackBuffer( + renderer, + vulkanCommandBuffer, + buffer + ); + } +} + +static void VULKAN_BindComputePipeline( + Refresh_CommandBuffer *commandBuffer, + Refresh_ComputePipeline *computePipeline +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanComputePipeline *vulkanComputePipeline = (VulkanComputePipeline*) computePipeline; + Uint32 i; + + renderer->vkCmdBindPipeline( + vulkanCommandBuffer->commandBuffer, + VK_PIPELINE_BIND_POINT_COMPUTE, + vulkanComputePipeline->pipeline + ); + + vulkanCommandBuffer->currentComputePipeline = vulkanComputePipeline; + + VULKAN_INTERNAL_TrackComputePipeline(renderer, vulkanCommandBuffer, vulkanComputePipeline); + + for (i = vulkanCommandBuffer->initializedComputeUniformBufferCount; i < vulkanComputePipeline->resourceLayout.uniformBufferCount; i += 1) + { + vulkanCommandBuffer->computeUniformBuffers[i] = VULKAN_INTERNAL_CreateUniformBuffer(renderer, UNIFORM_BUFFER_SIZE); + vulkanCommandBuffer->initializedComputeUniformBufferCount += 1; + } + + for (i = 0; i < vulkanComputePipeline->resourceLayout.uniformBufferCount; i += 1) + { + VULKAN_INTERNAL_TrackBuffer( + renderer, + vulkanCommandBuffer, + vulkanCommandBuffer->computeUniformBuffers[i]->bufferContainer->activeBufferHandle->vulkanBuffer + ); + } + + vulkanCommandBuffer->needNewComputeReadWriteDescriptorSet = SDL_TRUE; + vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = SDL_TRUE; + vulkanCommandBuffer->needNewComputeUniformDescriptorSet = SDL_TRUE; + vulkanCommandBuffer->needNewComputeUniformOffsets = SDL_TRUE; +} + +static void VULKAN_BindComputeStorageTextures( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_TextureSlice *storageTextureSlices, + Uint32 bindingCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + VulkanTextureContainer *textureContainer; + VulkanTextureSlice *textureSlice; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + if (vulkanCommandBuffer->readOnlyComputeStorageTextureSlices[firstSlot + i] != NULL) + { + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ, + vulkanCommandBuffer->readOnlyComputeStorageTextureSlices[firstSlot + i] + ); + } + + textureContainer = (VulkanTextureContainer*) storageTextureSlices[i].texture; + textureSlice = VULKAN_INTERNAL_FetchTextureSlice( + textureContainer->activeTextureHandle->vulkanTexture, + storageTextureSlices[i].layer, + storageTextureSlices[i].mipLevel + ); + + vulkanCommandBuffer->readOnlyComputeStorageTextureSlices[firstSlot + i] = textureSlice; + + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ, + textureSlice + ); + + VULKAN_INTERNAL_TrackTextureSlice( + renderer, + vulkanCommandBuffer, + textureSlice + ); + } + + vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = SDL_TRUE; +} + +static void VULKAN_BindComputeStorageBuffers( + Refresh_CommandBuffer *commandBuffer, + Uint32 firstSlot, + Refresh_Buffer **storageBuffers, + Uint32 bindingCount +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + VulkanBufferContainer *bufferContainer; + Uint32 i; + + for (i = 0; i < bindingCount; i += 1) + { + if (vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] != NULL) + { + VULKAN_INTERNAL_BufferTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ, + vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] + ); + } + + bufferContainer = (VulkanBufferContainer*) storageBuffers[i]; + + vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] = bufferContainer->activeBufferHandle->vulkanBuffer; + + VULKAN_INTERNAL_BufferTransitionFromDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ, + bufferContainer->activeBufferHandle->vulkanBuffer + ); + + VULKAN_INTERNAL_TrackBuffer( + renderer, + vulkanCommandBuffer, + bufferContainer->activeBufferHandle->vulkanBuffer + ); + } + + vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = SDL_TRUE; +} + +static void VULKAN_PushComputeUniformData( + Refresh_CommandBuffer *commandBuffer, + Uint32 slotIndex, + void *data, + Uint32 dataLengthInBytes +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + + if (slotIndex >= vulkanCommandBuffer->currentComputePipeline->resourceLayout.uniformBufferCount) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No compute uniforms exist on slot %i for this pipeline", slotIndex); + return; + } + + VULKAN_INTERNAL_PushUniformData( + vulkanCommandBuffer->renderer, + vulkanCommandBuffer, + vulkanCommandBuffer->computeUniformBuffers[slotIndex], + REFRESH_SHADERSTAGE_COMPUTE, + data, + dataLengthInBytes + ); +} + +static void VULKAN_INTERNAL_BindComputeDescriptorSets( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer +) { + VulkanComputePipelineResourceLayout *resourceLayout; + VkWriteDescriptorSet *writeDescriptorSets; + VkWriteDescriptorSet *currentWriteDescriptorSet; + DescriptorSetPool *descriptorSetPool; + VkDescriptorBufferInfo bufferInfos[MAX_STORAGE_BUFFERS_PER_STAGE]; + VkDescriptorImageInfo imageInfos[MAX_STORAGE_TEXTURES_PER_STAGE]; + Uint32 dynamicOffsets[MAX_UNIFORM_BUFFERS_PER_STAGE]; + Uint32 bufferInfoCount = 0; + Uint32 imageInfoCount = 0; + Uint32 i; + + resourceLayout = &commandBuffer->currentComputePipeline->resourceLayout; + + if (commandBuffer->needNewComputeReadOnlyDescriptorSet) + { + descriptorSetPool = &resourceLayout->descriptorSetPools[0]; + + commandBuffer->computeReadOnlyDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet( + renderer, + commandBuffer, + descriptorSetPool + ); + + writeDescriptorSets = SDL_stack_alloc( + VkWriteDescriptorSet, + resourceLayout->readOnlyStorageTextureCount + + resourceLayout->readOnlyStorageBufferCount + ); + + for (i = 0; i < resourceLayout->readOnlyStorageTextureCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[i]; + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = i; + currentWriteDescriptorSet->dstSet = commandBuffer->computeReadOnlyDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pBufferInfo = NULL; + + imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE; + imageInfos[imageInfoCount].imageView = commandBuffer->readOnlyComputeStorageTextureSlices[i]->view; + imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount]; + + imageInfoCount += 1; + } + + for (i = 0; i < resourceLayout->readOnlyStorageBufferCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->readOnlyStorageTextureCount + i]; + + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = resourceLayout->readOnlyStorageTextureCount + i; + currentWriteDescriptorSet->dstSet = commandBuffer->computeReadOnlyDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pImageInfo = NULL; + + bufferInfos[bufferInfoCount].buffer = commandBuffer->readOnlyComputeStorageBuffers[i]->buffer; + bufferInfos[bufferInfoCount].offset = 0; + bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE; + + currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount]; + + bufferInfoCount += 1; + } + + renderer->vkUpdateDescriptorSets( + renderer->logicalDevice, + resourceLayout->readOnlyStorageTextureCount + resourceLayout->readOnlyStorageBufferCount, + writeDescriptorSets, + 0, + NULL + ); + + renderer->vkCmdBindDescriptorSets( + commandBuffer->commandBuffer, + VK_PIPELINE_BIND_POINT_COMPUTE, + resourceLayout->pipelineLayout, + 0, + 1, + &commandBuffer->computeReadOnlyDescriptorSet, + 0, + NULL + ); + + SDL_stack_free(writeDescriptorSets); + bufferInfoCount = 0; + imageInfoCount = 0; + + commandBuffer->needNewComputeReadOnlyDescriptorSet = SDL_FALSE; + } + + if (commandBuffer->needNewComputeReadWriteDescriptorSet) + { + descriptorSetPool = &resourceLayout->descriptorSetPools[1]; + + commandBuffer->computeReadWriteDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet( + renderer, + commandBuffer, + descriptorSetPool + ); + + writeDescriptorSets = SDL_stack_alloc( + VkWriteDescriptorSet, + resourceLayout->readWriteStorageTextureCount + + resourceLayout->readWriteStorageBufferCount + ); + + for (i = 0; i < resourceLayout->readWriteStorageTextureCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[i]; + + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = i; + currentWriteDescriptorSet->dstSet = commandBuffer->computeReadWriteDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pBufferInfo = NULL; + + imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE; + imageInfos[imageInfoCount].imageView = commandBuffer->readWriteComputeStorageTextureSlices[i]->view; + imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount]; + + imageInfoCount += 1; + } + + for (i = 0; i < resourceLayout->readWriteStorageBufferCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[resourceLayout->readWriteStorageTextureCount + i]; + + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = resourceLayout->readWriteStorageTextureCount + i; + currentWriteDescriptorSet->dstSet = commandBuffer->computeReadWriteDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pImageInfo = NULL; + + bufferInfos[bufferInfoCount].buffer = commandBuffer->readWriteComputeStorageBuffers[i]->buffer; + bufferInfos[bufferInfoCount].offset = 0; + bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE; + + currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount]; + + bufferInfoCount += 1; + } + + renderer->vkUpdateDescriptorSets( + renderer->logicalDevice, + resourceLayout->readWriteStorageTextureCount + resourceLayout->readWriteStorageBufferCount, + writeDescriptorSets, + 0, + NULL + ); + + renderer->vkCmdBindDescriptorSets( + commandBuffer->commandBuffer, + VK_PIPELINE_BIND_POINT_COMPUTE, + resourceLayout->pipelineLayout, + 1, + 1, + &commandBuffer->computeReadWriteDescriptorSet, + 0, + NULL + ); + + SDL_stack_free(writeDescriptorSets); + bufferInfoCount = 0; + imageInfoCount = 0; + + commandBuffer->needNewComputeReadWriteDescriptorSet = SDL_FALSE; + } + + if (commandBuffer->needNewComputeUniformDescriptorSet) + { + descriptorSetPool = &resourceLayout->descriptorSetPools[2]; + + commandBuffer->computeUniformDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet( + renderer, + commandBuffer, + descriptorSetPool + ); + + writeDescriptorSets = SDL_stack_alloc( + VkWriteDescriptorSet, + resourceLayout->uniformBufferCount + ); + + for (i = 0; i < resourceLayout->uniformBufferCount; i += 1) + { + currentWriteDescriptorSet = &writeDescriptorSets[i]; + + currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + currentWriteDescriptorSet->pNext = NULL; + currentWriteDescriptorSet->descriptorCount = 1; + currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + currentWriteDescriptorSet->dstArrayElement = 0; + currentWriteDescriptorSet->dstBinding = i; + currentWriteDescriptorSet->dstSet = commandBuffer->computeUniformDescriptorSet; + currentWriteDescriptorSet->pTexelBufferView = NULL; + currentWriteDescriptorSet->pImageInfo = NULL; + + bufferInfos[bufferInfoCount].buffer = commandBuffer->computeUniformBuffers[i]->bufferContainer->activeBufferHandle->vulkanBuffer->buffer; + bufferInfos[bufferInfoCount].offset = 0; + bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE; + + currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount]; + + bufferInfoCount += 1; + } + + renderer->vkUpdateDescriptorSets( + renderer->logicalDevice, + resourceLayout->uniformBufferCount, + writeDescriptorSets, + 0, + NULL + ); + + SDL_stack_free(writeDescriptorSets); + bufferInfoCount = 0; + imageInfoCount = 0; + + commandBuffer->needNewComputeUniformDescriptorSet = SDL_FALSE; + commandBuffer->needNewComputeUniformOffsets = SDL_TRUE; + } + + if (commandBuffer->needNewComputeUniformOffsets) + { + for (i = 0; i < resourceLayout->uniformBufferCount; i += 1) + { + dynamicOffsets[i] = commandBuffer->computeUniformBuffers[i]->drawOffset; + } + + renderer->vkCmdBindDescriptorSets( + commandBuffer->commandBuffer, + VK_PIPELINE_BIND_POINT_COMPUTE, + resourceLayout->pipelineLayout, + 2, + 1, + &commandBuffer->computeUniformDescriptorSet, + resourceLayout->uniformBufferCount, + dynamicOffsets + ); + + commandBuffer->needNewComputeUniformOffsets = SDL_FALSE; + } +} + +static void VULKAN_DispatchCompute( + Refresh_CommandBuffer *commandBuffer, + Uint32 groupCountX, + Uint32 groupCountY, + Uint32 groupCountZ +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + + VULKAN_INTERNAL_BindComputeDescriptorSets(renderer, vulkanCommandBuffer); + + renderer->vkCmdDispatch( + vulkanCommandBuffer->commandBuffer, + groupCountX, + groupCountY, + groupCountZ + ); +} + +static void VULKAN_EndComputePass( + Refresh_CommandBuffer *commandBuffer +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + Uint32 i; + + for (i = 0; i < MAX_STORAGE_TEXTURES_PER_STAGE; i += 1) + { + if (vulkanCommandBuffer->readWriteComputeStorageTextureSlices[i] != NULL) + { + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + vulkanCommandBuffer->renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE, + vulkanCommandBuffer->readWriteComputeStorageTextureSlices[i] + ); + + vulkanCommandBuffer->readWriteComputeStorageTextureSlices[i] = NULL; + } + } + + for (i = 0; i < MAX_STORAGE_BUFFERS_PER_STAGE; i += 1) + { + if (vulkanCommandBuffer->readWriteComputeStorageBuffers[i] != NULL) + { + VULKAN_INTERNAL_BufferTransitionToDefaultUsage( + vulkanCommandBuffer->renderer, + vulkanCommandBuffer, + VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE, + vulkanCommandBuffer->readWriteComputeStorageBuffers[i] + ); + + vulkanCommandBuffer->readWriteComputeStorageBuffers[i] = NULL; + } + } + + for (i = 0; i < MAX_STORAGE_TEXTURES_PER_STAGE; i += 1) + { + if (vulkanCommandBuffer->readOnlyComputeStorageTextureSlices[i] != NULL) + { + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + vulkanCommandBuffer->renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ, + vulkanCommandBuffer->readOnlyComputeStorageTextureSlices[i] + ); + + vulkanCommandBuffer->readOnlyComputeStorageTextureSlices[i] = NULL; + } + } + + for (i = 0; i < MAX_STORAGE_BUFFERS_PER_STAGE; i += 1) + { + if (vulkanCommandBuffer->readOnlyComputeStorageBuffers[i] != NULL) + { + VULKAN_INTERNAL_BufferTransitionToDefaultUsage( + vulkanCommandBuffer->renderer, + vulkanCommandBuffer, + VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ, + vulkanCommandBuffer->readOnlyComputeStorageBuffers[i] + ); + + vulkanCommandBuffer->readOnlyComputeStorageBuffers[i] = NULL; + } + } + + vulkanCommandBuffer->currentComputePipeline = NULL; + + vulkanCommandBuffer->computeReadOnlyDescriptorSet = VK_NULL_HANDLE; + vulkanCommandBuffer->computeReadWriteDescriptorSet = VK_NULL_HANDLE; + vulkanCommandBuffer->computeUniformDescriptorSet = VK_NULL_HANDLE; +} + +static void VULKAN_MapTransferBuffer( + Refresh_Renderer *driverData, + Refresh_TransferBuffer *transferBuffer, + SDL_bool cycle, + void **ppData +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; + + if ( + cycle && + SDL_AtomicGet(&transferBufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0 + ) { + VULKAN_INTERNAL_CycleActiveBuffer( + renderer, + transferBufferContainer + ); + } + + Uint8 *bufferPointer = + transferBufferContainer->activeBufferHandle->vulkanBuffer->usedRegion->allocation->mapPointer + + transferBufferContainer->activeBufferHandle->vulkanBuffer->usedRegion->resourceOffset; + + *ppData = bufferPointer; +} + +static void VULKAN_UnmapTransferBuffer( + Refresh_Renderer *driverData, + Refresh_TransferBuffer *transferBuffer +) { + /* no-op because transfer buffers are persistently mapped */ + (void)driverData; + (void)transferBuffer; +} + +static void VULKAN_SetTransferData( + Refresh_Renderer *driverData, + void* data, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; + + if ( + cycle && + SDL_AtomicGet(&transferBufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0 + ) { + VULKAN_INTERNAL_CycleActiveBuffer( + renderer, + transferBufferContainer + ); + } + + Uint8 *bufferPointer = + transferBufferContainer->activeBufferHandle->vulkanBuffer->usedRegion->allocation->mapPointer + + transferBufferContainer->activeBufferHandle->vulkanBuffer->usedRegion->resourceOffset + + copyParams->dstOffset; + + SDL_memcpy( + bufferPointer, + ((Uint8*) data) + copyParams->srcOffset, + copyParams->size + ); +} + +static void VULKAN_GetTransferData( + Refresh_Renderer *driverData, + Refresh_TransferBuffer *transferBuffer, + void* data, + Refresh_BufferCopy *copyParams +) { + (void) driverData; /* used by other backends */ + VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; + VulkanBuffer *vulkanBuffer = transferBufferContainer->activeBufferHandle->vulkanBuffer; + + Uint8 *bufferPointer = + vulkanBuffer->usedRegion->allocation->mapPointer + + vulkanBuffer->usedRegion->resourceOffset + + copyParams->srcOffset; + + SDL_memcpy( + ((Uint8*) data) + copyParams->dstOffset, + bufferPointer, + copyParams->size + ); +} + +static void VULKAN_BeginCopyPass( + Refresh_CommandBuffer *commandBuffer +) { + /* no-op */ + (void)commandBuffer; +} + +static void VULKAN_UploadToTexture( + Refresh_CommandBuffer *commandBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_TextureRegion *textureRegion, + Refresh_BufferImageCopy *copyParams, + SDL_bool cycle +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; + VulkanTextureContainer *vulkanTextureContainer = (VulkanTextureContainer*) textureRegion->textureSlice.texture; + VulkanTextureSlice *vulkanTextureSlice; + VkBufferImageCopy imageCopy; + + /* Note that the transfer buffer does not need a barrier, as it is synced by the client */ + + vulkanTextureSlice = VULKAN_INTERNAL_PrepareTextureSliceForWrite( + renderer, + vulkanCommandBuffer, + vulkanTextureContainer, + textureRegion->textureSlice.layer, + textureRegion->textureSlice.mipLevel, + cycle, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION + ); + + imageCopy.imageExtent.width = textureRegion->w; + imageCopy.imageExtent.height = textureRegion->h; + imageCopy.imageExtent.depth = textureRegion->d; + imageCopy.imageOffset.x = textureRegion->x; + imageCopy.imageOffset.y = textureRegion->y; + imageCopy.imageOffset.z = textureRegion->z; + imageCopy.imageSubresource.aspectMask = vulkanTextureSlice->parent->aspectFlags; + imageCopy.imageSubresource.baseArrayLayer = textureRegion->textureSlice.layer; + imageCopy.imageSubresource.layerCount = 1; + imageCopy.imageSubresource.mipLevel = textureRegion->textureSlice.mipLevel; + imageCopy.bufferOffset = copyParams->bufferOffset; + imageCopy.bufferRowLength = copyParams->bufferStride; + imageCopy.bufferImageHeight = copyParams->bufferImageHeight; + + renderer->vkCmdCopyBufferToImage( + vulkanCommandBuffer->commandBuffer, + transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer, + vulkanTextureSlice->parent->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &imageCopy + ); + + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, + vulkanTextureSlice + ); + + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer); + VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, vulkanTextureSlice); +} + +static void VULKAN_UploadToBuffer( + Refresh_CommandBuffer *commandBuffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_Buffer *buffer, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; + VulkanBufferContainer *bufferContainer = (VulkanBufferContainer*) buffer; + VkBufferCopy bufferCopy; + + /* Note that the transfer buffer does not need a barrier, as it is synced by the client */ + + VulkanBuffer *vulkanBuffer = VULKAN_INTERNAL_PrepareBufferForWrite( + renderer, + vulkanCommandBuffer, + bufferContainer, + cycle, + VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION + ); + + bufferCopy.srcOffset = copyParams->srcOffset; + bufferCopy.dstOffset = copyParams->dstOffset; + bufferCopy.size = copyParams->size; + + renderer->vkCmdCopyBuffer( + vulkanCommandBuffer->commandBuffer, + transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer, + vulkanBuffer->buffer, + 1, + &bufferCopy + ); + + VULKAN_INTERNAL_BufferTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION, + vulkanBuffer + ); + + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer); + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, vulkanBuffer); +} + +/* Readback */ + +static void VULKAN_DownloadFromTexture( + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *textureRegion, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferImageCopy *copyParams +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + VulkanTextureSlice *vulkanTextureSlice; + VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; + VkBufferImageCopy imageCopy; + vulkanTextureSlice = VULKAN_INTERNAL_SDLToVulkanTextureSlice(&textureRegion->textureSlice); + + /* Note that the transfer buffer does not need a barrier, as it is synced by the client */ + + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, + vulkanTextureSlice + ); + + imageCopy.imageExtent.width = textureRegion->w; + imageCopy.imageExtent.height = textureRegion->h; + imageCopy.imageExtent.depth = textureRegion->d; + imageCopy.imageOffset.x = textureRegion->x; + imageCopy.imageOffset.y = textureRegion->y; + imageCopy.imageOffset.z = textureRegion->z; + imageCopy.imageSubresource.aspectMask = vulkanTextureSlice->parent->aspectFlags; + imageCopy.imageSubresource.baseArrayLayer = textureRegion->textureSlice.layer; + imageCopy.imageSubresource.layerCount = 1; + imageCopy.imageSubresource.mipLevel = textureRegion->textureSlice.mipLevel; + imageCopy.bufferOffset = copyParams->bufferOffset; + imageCopy.bufferRowLength = copyParams->bufferStride; + imageCopy.bufferImageHeight = copyParams->bufferImageHeight; + + renderer->vkCmdCopyImageToBuffer( + vulkanCommandBuffer->commandBuffer, + vulkanTextureSlice->parent->image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer, + 1, + &imageCopy + ); + + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, + vulkanTextureSlice + ); + + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer); + VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, vulkanTextureSlice); +} + +static void VULKAN_DownloadFromBuffer( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *buffer, + Refresh_TransferBuffer *transferBuffer, + Refresh_BufferCopy *copyParams +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = vulkanCommandBuffer->renderer; + VulkanBufferContainer *bufferContainer = (VulkanBufferContainer*) buffer; + VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer*) transferBuffer; + VkBufferCopy bufferCopy; + + /* Note that transfer buffer does not need a barrier, as it is synced by the client */ + + VULKAN_INTERNAL_BufferTransitionFromDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE, + bufferContainer->activeBufferHandle->vulkanBuffer + ); + + bufferCopy.srcOffset = copyParams->srcOffset; + bufferCopy.dstOffset = copyParams->dstOffset; + bufferCopy.size = copyParams->size; + + renderer->vkCmdCopyBuffer( + vulkanCommandBuffer->commandBuffer, + bufferContainer->activeBufferHandle->vulkanBuffer->buffer, + transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer, + 1, + &bufferCopy + ); + + VULKAN_INTERNAL_BufferTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE, + bufferContainer->activeBufferHandle->vulkanBuffer + ); + + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer); + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, bufferContainer->activeBufferHandle->vulkanBuffer); +} + +static void VULKAN_CopyTextureToTexture( + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *source, + Refresh_TextureRegion *destination, + SDL_bool cycle +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanTextureContainer *dstContainer = (VulkanTextureContainer*) destination->textureSlice.texture; + VulkanTextureSlice *srcSlice; + VulkanTextureSlice *dstSlice; + VkImageCopy imageCopy; + + srcSlice = VULKAN_INTERNAL_SDLToVulkanTextureSlice(&source->textureSlice); + + dstSlice = VULKAN_INTERNAL_PrepareTextureSliceForWrite( + renderer, + vulkanCommandBuffer, + dstContainer, + destination->textureSlice.layer, + destination->textureSlice.mipLevel, + cycle, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION + ); + + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, + srcSlice + ); + + imageCopy.srcOffset.x = source->x; + imageCopy.srcOffset.y = source->y; + imageCopy.srcOffset.z = source->z; + imageCopy.srcSubresource.aspectMask = srcSlice->parent->aspectFlags; + imageCopy.srcSubresource.baseArrayLayer = source->textureSlice.layer; + imageCopy.srcSubresource.layerCount = 1; + imageCopy.srcSubresource.mipLevel = source->textureSlice.mipLevel; + imageCopy.dstOffset.x = destination->x; + imageCopy.dstOffset.y = destination->y; + imageCopy.dstOffset.z = destination->z; + imageCopy.dstSubresource.aspectMask = dstSlice->parent->aspectFlags; + imageCopy.dstSubresource.baseArrayLayer = destination->textureSlice.layer; + imageCopy.dstSubresource.layerCount = 1; + imageCopy.dstSubresource.mipLevel = destination->textureSlice.mipLevel; + imageCopy.extent.width = source->w; + imageCopy.extent.height = source->h; + imageCopy.extent.depth = source->d; + + renderer->vkCmdCopyImage( + vulkanCommandBuffer->commandBuffer, + srcSlice->parent->image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dstSlice->parent->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &imageCopy + ); + + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, + srcSlice + ); + + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, + dstSlice + ); + + VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, srcSlice); + VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, dstSlice); +} + +static void VULKAN_CopyBufferToBuffer( + Refresh_CommandBuffer *commandBuffer, + Refresh_Buffer *source, + Refresh_Buffer *destination, + Refresh_BufferCopy *copyParams, + SDL_bool cycle +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanBufferContainer *srcContainer = (VulkanBufferContainer*) source; + VulkanBufferContainer *dstContainer = (VulkanBufferContainer*) destination; + VkBufferCopy bufferCopy; + + VulkanBuffer *dstBuffer = VULKAN_INTERNAL_PrepareBufferForWrite( + renderer, + vulkanCommandBuffer, + dstContainer, + cycle, + VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION + ); + + VULKAN_INTERNAL_BufferTransitionFromDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE, + srcContainer->activeBufferHandle->vulkanBuffer + ); + + bufferCopy.srcOffset = copyParams->srcOffset; + bufferCopy.dstOffset = copyParams->dstOffset; + bufferCopy.size = copyParams->size; + + renderer->vkCmdCopyBuffer( + vulkanCommandBuffer->commandBuffer, + srcContainer->activeBufferHandle->vulkanBuffer->buffer, + dstBuffer->buffer, + 1, + &bufferCopy + ); + + VULKAN_INTERNAL_BufferTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE, + srcContainer->activeBufferHandle->vulkanBuffer + ); + + VULKAN_INTERNAL_BufferTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION, + dstBuffer + ); + + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, srcContainer->activeBufferHandle->vulkanBuffer); + VULKAN_INTERNAL_TrackBuffer(renderer, vulkanCommandBuffer, dstBuffer); +} + +static void VULKAN_GenerateMipmaps( + Refresh_CommandBuffer *commandBuffer, + Refresh_Texture *texture +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanTexture *vulkanTexture = ((VulkanTextureContainer*) texture)->activeTextureHandle->vulkanTexture; + VulkanTextureSlice *srcTextureSlice; + VulkanTextureSlice *dstTextureSlice; + VkImageBlit blit; + Uint32 layer, level; + + if (vulkanTexture->levelCount <= 1) { return; } + + /* Blit each slice sequentially. Barriers, barriers everywhere! */ + for (layer = 0; layer < vulkanTexture->layerCount; layer += 1) + for (level = 1; level < vulkanTexture->levelCount; level += 1) + { + srcTextureSlice = VULKAN_INTERNAL_FetchTextureSlice( + vulkanTexture, + layer, + level - 1 + ); + + dstTextureSlice = VULKAN_INTERNAL_FetchTextureSlice( + vulkanTexture, + layer, + level + ); + + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, + srcTextureSlice + ); + + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, + dstTextureSlice + ); + + blit.srcOffsets[0].x = 0; + blit.srcOffsets[0].y = 0; + blit.srcOffsets[0].z = 0; + + blit.srcOffsets[1].x = vulkanTexture->dimensions.width >> (level - 1); + blit.srcOffsets[1].y = vulkanTexture->dimensions.height >> (level - 1); + blit.srcOffsets[1].z = 1; + + blit.dstOffsets[0].x = 0; + blit.dstOffsets[0].y = 0; + blit.dstOffsets[0].z = 0; + + blit.dstOffsets[1].x = vulkanTexture->dimensions.width >> level; + blit.dstOffsets[1].y = vulkanTexture->dimensions.height >> level; + blit.dstOffsets[1].z = 1; + + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.srcSubresource.baseArrayLayer = layer; + blit.srcSubresource.layerCount = 1; + blit.srcSubresource.mipLevel = level - 1; + + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.baseArrayLayer = layer; + blit.dstSubresource.layerCount = 1; + blit.dstSubresource.mipLevel = level; + + renderer->vkCmdBlitImage( + vulkanCommandBuffer->commandBuffer, + vulkanTexture->image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + vulkanTexture->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &blit, + VK_FILTER_LINEAR + ); + + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, + srcTextureSlice + ); + + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, + dstTextureSlice + ); + + VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, srcTextureSlice); + VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, dstTextureSlice); + } +} + +static void VULKAN_EndCopyPass( + Refresh_CommandBuffer *commandBuffer +) { + /* no-op */ + (void) commandBuffer; +} + +static void VULKAN_Blit( + Refresh_CommandBuffer *commandBuffer, + Refresh_TextureRegion *source, + Refresh_TextureRegion *destination, + Refresh_Filter filterMode, + SDL_bool cycle +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanTextureContainer *sourceTextureContainer = (VulkanTextureContainer*) source->textureSlice.texture; + VkImageBlit region; + + VulkanTextureSlice *srcTextureSlice = VULKAN_INTERNAL_FetchTextureSlice( + sourceTextureContainer->activeTextureHandle->vulkanTexture, + source->textureSlice.layer, + source->textureSlice.mipLevel + ); + + VulkanTextureSlice *dstTextureSlice = VULKAN_INTERNAL_PrepareTextureSliceForWrite( + renderer, + vulkanCommandBuffer, + (VulkanTextureContainer*) destination->textureSlice.texture, + destination->textureSlice.layer, + destination->textureSlice.mipLevel, + cycle, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION + ); + + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, + srcTextureSlice + ); + + region.srcSubresource.aspectMask = srcTextureSlice->parent->aspectFlags; + region.srcSubresource.baseArrayLayer = srcTextureSlice->layer; + region.srcSubresource.layerCount = 1; + region.srcSubresource.mipLevel = srcTextureSlice->level; + region.srcOffsets[0].x = source->x; + region.srcOffsets[0].y = source->y; + region.srcOffsets[0].z = source->z; + region.srcOffsets[1].x = source->x + source->w; + region.srcOffsets[1].y = source->y + source->h; + region.srcOffsets[1].z = source->z + source->d; + + region.dstSubresource.aspectMask = dstTextureSlice->parent->aspectFlags; + region.dstSubresource.baseArrayLayer = dstTextureSlice->layer; + region.dstSubresource.layerCount = 1; + region.dstSubresource.mipLevel = dstTextureSlice->level; + region.dstOffsets[0].x = destination->x; + region.dstOffsets[0].y = destination->y; + region.dstOffsets[0].z = destination->z; + region.dstOffsets[1].x = destination->x + destination->w; + region.dstOffsets[1].y = destination->y + destination->h; + region.dstOffsets[1].z = destination->z + destination->d; + + renderer->vkCmdBlitImage( + vulkanCommandBuffer->commandBuffer, + srcTextureSlice->parent->image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dstTextureSlice->parent->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + ®ion, + SDLToVK_Filter[filterMode] + ); + + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, + srcTextureSlice + ); + + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, + dstTextureSlice + ); + + VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, srcTextureSlice); + VULKAN_INTERNAL_TrackTextureSlice(renderer, vulkanCommandBuffer, dstTextureSlice); +} + +static void VULKAN_INTERNAL_AllocateCommandBuffers( + VulkanRenderer *renderer, + VulkanCommandPool *vulkanCommandPool, + Uint32 allocateCount +) { + VkCommandBufferAllocateInfo allocateInfo; + VkResult vulkanResult; + Uint32 i; + VkCommandBuffer *commandBuffers = SDL_stack_alloc(VkCommandBuffer, allocateCount); + VulkanCommandBuffer *commandBuffer; + + vulkanCommandPool->inactiveCommandBufferCapacity += allocateCount; + + vulkanCommandPool->inactiveCommandBuffers = SDL_realloc( + vulkanCommandPool->inactiveCommandBuffers, + sizeof(VulkanCommandBuffer*) * + vulkanCommandPool->inactiveCommandBufferCapacity + ); + + allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocateInfo.pNext = NULL; + allocateInfo.commandPool = vulkanCommandPool->commandPool; + allocateInfo.commandBufferCount = allocateCount; + allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + + vulkanResult = renderer->vkAllocateCommandBuffers( + renderer->logicalDevice, + &allocateInfo, + commandBuffers + ); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkAllocateCommandBuffers", vulkanResult); + SDL_stack_free(commandBuffers); + return; + } + + for (i = 0; i < allocateCount; i += 1) + { + commandBuffer = SDL_malloc(sizeof(VulkanCommandBuffer)); + commandBuffer->renderer = renderer; + commandBuffer->commandPool = vulkanCommandPool; + commandBuffer->commandBuffer = commandBuffers[i]; + + commandBuffer->inFlightFence = VK_NULL_HANDLE; + + /* Presentation tracking */ + + commandBuffer->presentDataCapacity = 1; + commandBuffer->presentDataCount = 0; + commandBuffer->presentDatas = SDL_malloc( + commandBuffer->presentDataCapacity * sizeof(VkPresentInfoKHR) + ); + + commandBuffer->waitSemaphoreCapacity = 1; + commandBuffer->waitSemaphoreCount = 0; + commandBuffer->waitSemaphores = SDL_malloc( + commandBuffer->waitSemaphoreCapacity * sizeof(VkSemaphore) + ); + + commandBuffer->signalSemaphoreCapacity = 1; + commandBuffer->signalSemaphoreCount = 0; + commandBuffer->signalSemaphores = SDL_malloc( + commandBuffer->signalSemaphoreCapacity * sizeof(VkSemaphore) + ); + + /* Descriptor set tracking */ + + commandBuffer->boundDescriptorSetDataCapacity = 16; + commandBuffer->boundDescriptorSetDataCount = 0; + commandBuffer->boundDescriptorSetDatas = SDL_malloc( + commandBuffer->boundDescriptorSetDataCapacity * sizeof(DescriptorSetData) + ); + + /* Resource bind tracking */ + + commandBuffer->needNewVertexResourceDescriptorSet = SDL_TRUE; + commandBuffer->needNewVertexUniformDescriptorSet = SDL_TRUE; + commandBuffer->needNewVertexUniformOffsets = SDL_TRUE; + commandBuffer->needNewFragmentResourceDescriptorSet = SDL_TRUE; + commandBuffer->needNewFragmentUniformDescriptorSet = SDL_TRUE; + commandBuffer->needNewFragmentUniformOffsets = SDL_TRUE; + + commandBuffer->needNewComputeReadWriteDescriptorSet = SDL_TRUE; + commandBuffer->needNewComputeReadOnlyDescriptorSet = SDL_TRUE; + commandBuffer->needNewComputeUniformDescriptorSet = SDL_TRUE; + commandBuffer->needNewComputeUniformOffsets = SDL_TRUE; + + commandBuffer->vertexResourceDescriptorSet = VK_NULL_HANDLE; + commandBuffer->vertexUniformDescriptorSet = VK_NULL_HANDLE; + commandBuffer->fragmentResourceDescriptorSet = VK_NULL_HANDLE; + commandBuffer->fragmentUniformDescriptorSet = VK_NULL_HANDLE; + + commandBuffer->computeReadOnlyDescriptorSet = VK_NULL_HANDLE; + commandBuffer->computeReadWriteDescriptorSet = VK_NULL_HANDLE; + commandBuffer->computeUniformDescriptorSet = VK_NULL_HANDLE; + + /* Uniform buffers */ + + commandBuffer->initializedVertexUniformBufferCount = 0; + commandBuffer->initializedFragmentUniformBufferCount = 0; + commandBuffer->initializedComputeUniformBufferCount = 0; + + /* Resource tracking */ + + commandBuffer->usedBufferCapacity = 4; + commandBuffer->usedBufferCount = 0; + commandBuffer->usedBuffers = SDL_malloc( + commandBuffer->usedBufferCapacity * sizeof(VulkanBuffer*) + ); + + commandBuffer->usedTextureSliceCapacity = 4; + commandBuffer->usedTextureSliceCount = 0; + commandBuffer->usedTextureSlices = SDL_malloc( + commandBuffer->usedTextureSliceCapacity * sizeof(VulkanTextureSlice*) + ); + + commandBuffer->usedSamplerCapacity = 4; + commandBuffer->usedSamplerCount = 0; + commandBuffer->usedSamplers = SDL_malloc( + commandBuffer->usedSamplerCapacity * sizeof(VulkanSampler*) + ); + + commandBuffer->usedGraphicsPipelineCapacity = 4; + commandBuffer->usedGraphicsPipelineCount = 0; + commandBuffer->usedGraphicsPipelines = SDL_malloc( + commandBuffer->usedGraphicsPipelineCapacity * sizeof(VulkanGraphicsPipeline*) + ); + + commandBuffer->usedComputePipelineCapacity = 4; + commandBuffer->usedComputePipelineCount = 0; + commandBuffer->usedComputePipelines = SDL_malloc( + commandBuffer->usedComputePipelineCapacity * sizeof(VulkanComputePipeline*) + ); + + commandBuffer->usedFramebufferCapacity = 4; + commandBuffer->usedFramebufferCount = 0; + commandBuffer->usedFramebuffers = SDL_malloc( + commandBuffer->usedFramebufferCapacity * sizeof(VulkanFramebuffer*) + ); + + /* Pool it! */ + + vulkanCommandPool->inactiveCommandBuffers[ + vulkanCommandPool->inactiveCommandBufferCount + ] = commandBuffer; + vulkanCommandPool->inactiveCommandBufferCount += 1; + } + + SDL_stack_free(commandBuffers); +} + +static VulkanCommandPool* VULKAN_INTERNAL_FetchCommandPool( + VulkanRenderer *renderer, + SDL_threadID threadID +) { + VulkanCommandPool *vulkanCommandPool; + VkCommandPoolCreateInfo commandPoolCreateInfo; + VkResult vulkanResult; + CommandPoolHash commandPoolHash; + + commandPoolHash.threadID = threadID; + + vulkanCommandPool = CommandPoolHashTable_Fetch( + &renderer->commandPoolHashTable, + commandPoolHash + ); + + if (vulkanCommandPool != NULL) + { + return vulkanCommandPool; + } + + vulkanCommandPool = (VulkanCommandPool*) SDL_malloc(sizeof(VulkanCommandPool)); + + commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + commandPoolCreateInfo.pNext = NULL; + commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + commandPoolCreateInfo.queueFamilyIndex = renderer->queueFamilyIndex; + + vulkanResult = renderer->vkCreateCommandPool( + renderer->logicalDevice, + &commandPoolCreateInfo, + NULL, + &vulkanCommandPool->commandPool + ); + + if (vulkanResult != VK_SUCCESS) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create command pool!"); + LogVulkanResultAsError("vkCreateCommandPool", vulkanResult); + return NULL; + } + + vulkanCommandPool->threadID = threadID; + + vulkanCommandPool->inactiveCommandBufferCapacity = 0; + vulkanCommandPool->inactiveCommandBufferCount = 0; + vulkanCommandPool->inactiveCommandBuffers = NULL; + + VULKAN_INTERNAL_AllocateCommandBuffers( + renderer, + vulkanCommandPool, + 2 + ); + + CommandPoolHashTable_Insert( + &renderer->commandPoolHashTable, + commandPoolHash, + vulkanCommandPool + ); + + return vulkanCommandPool; +} + +static VulkanCommandBuffer* VULKAN_INTERNAL_GetInactiveCommandBufferFromPool( + VulkanRenderer *renderer, + SDL_threadID threadID +) { + VulkanCommandPool *commandPool = + VULKAN_INTERNAL_FetchCommandPool(renderer, threadID); + VulkanCommandBuffer *commandBuffer; + + if (commandPool == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to fetch command pool!"); + return NULL; + } + + if (commandPool->inactiveCommandBufferCount == 0) + { + VULKAN_INTERNAL_AllocateCommandBuffers( + renderer, + commandPool, + commandPool->inactiveCommandBufferCapacity + ); + } + + commandBuffer = commandPool->inactiveCommandBuffers[commandPool->inactiveCommandBufferCount - 1]; + commandPool->inactiveCommandBufferCount -= 1; + + return commandBuffer; +} + +static Refresh_CommandBuffer* VULKAN_AcquireCommandBuffer( + Refresh_Renderer *driverData +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VkResult result; + Uint32 i; + + SDL_threadID threadID = SDL_ThreadID(); + + SDL_LockMutex(renderer->acquireCommandBufferLock); + + VulkanCommandBuffer *commandBuffer = + VULKAN_INTERNAL_GetInactiveCommandBufferFromPool(renderer, threadID); + + SDL_UnlockMutex(renderer->acquireCommandBufferLock); + + if (commandBuffer == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to acquire command buffer!"); + return NULL; + } + + /* Reset state */ + + commandBuffer->currentComputePipeline = NULL; + commandBuffer->currentGraphicsPipeline = NULL; + + for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) + { + commandBuffer->colorAttachmentSlices[i] = NULL; + } + + commandBuffer->depthStencilAttachmentSlice = NULL; + + commandBuffer->needNewVertexResourceDescriptorSet = SDL_TRUE; + commandBuffer->needNewVertexUniformDescriptorSet = SDL_TRUE; + commandBuffer->needNewVertexUniformOffsets = SDL_TRUE; + commandBuffer->needNewFragmentResourceDescriptorSet = SDL_TRUE; + commandBuffer->needNewFragmentUniformDescriptorSet = SDL_TRUE; + commandBuffer->needNewFragmentUniformOffsets = SDL_TRUE; + + commandBuffer->needNewComputeReadOnlyDescriptorSet = SDL_TRUE; + commandBuffer->needNewComputeUniformDescriptorSet = SDL_TRUE; + commandBuffer->needNewComputeUniformOffsets = SDL_TRUE; + + commandBuffer->vertexResourceDescriptorSet = VK_NULL_HANDLE; + commandBuffer->vertexUniformDescriptorSet = VK_NULL_HANDLE; + commandBuffer->fragmentResourceDescriptorSet = VK_NULL_HANDLE; + commandBuffer->fragmentUniformDescriptorSet = VK_NULL_HANDLE; + + commandBuffer->computeReadOnlyDescriptorSet = VK_NULL_HANDLE; + commandBuffer->computeReadWriteDescriptorSet = VK_NULL_HANDLE; + commandBuffer->computeUniformDescriptorSet = VK_NULL_HANDLE; + + SDL_zeroa(commandBuffer->vertexSamplerTextures); + SDL_zeroa(commandBuffer->vertexSamplers); + SDL_zeroa(commandBuffer->vertexStorageTextureSlices); + SDL_zeroa(commandBuffer->vertexStorageBuffers); + + SDL_zeroa(commandBuffer->fragmentSamplerTextures); + SDL_zeroa(commandBuffer->fragmentSamplers); + SDL_zeroa(commandBuffer->fragmentStorageTextureSlices); + SDL_zeroa(commandBuffer->fragmentStorageBuffers); + + SDL_zeroa(commandBuffer->readWriteComputeStorageTextureSlices); + SDL_zeroa(commandBuffer->readWriteComputeStorageBuffers); + SDL_zeroa(commandBuffer->readOnlyComputeStorageTextureSlices); + SDL_zeroa(commandBuffer->readOnlyComputeStorageBuffers); + + commandBuffer->autoReleaseFence = 1; + + commandBuffer->isDefrag = 0; + + /* Reset the command buffer here to avoid resets being called + * from a separate thread than where the command buffer was acquired + */ + result = renderer->vkResetCommandBuffer( + commandBuffer->commandBuffer, + VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT + ); + + if (result != VK_SUCCESS) + { + LogVulkanResultAsError("vkResetCommandBuffer", result); + } + + VULKAN_INTERNAL_BeginCommandBuffer(renderer, commandBuffer); + + return (Refresh_CommandBuffer*) commandBuffer; +} + +static SDL_bool VULKAN_QueryFence( + Refresh_Renderer *driverData, + Refresh_Fence *fence +) { + VulkanRenderer* renderer = (VulkanRenderer*) driverData; + VkResult result; + + result = renderer->vkGetFenceStatus( + renderer->logicalDevice, + ((VulkanFenceHandle*) fence)->fence + ); + + if (result == VK_SUCCESS) + { + return 1; + } + else if (result == VK_NOT_READY) + { + return 0; + } + else + { + LogVulkanResultAsError("vkGetFenceStatus", result); + return 0; + } +} + +static void VULKAN_INTERNAL_ReturnFenceToPool( + VulkanRenderer *renderer, + VulkanFenceHandle *fenceHandle +) { + SDL_LockMutex(renderer->fencePool.lock); + + EXPAND_ARRAY_IF_NEEDED( + renderer->fencePool.availableFences, + VulkanFenceHandle*, + renderer->fencePool.availableFenceCount + 1, + renderer->fencePool.availableFenceCapacity, + renderer->fencePool.availableFenceCapacity * 2 + ); + + renderer->fencePool.availableFences[renderer->fencePool.availableFenceCount] = fenceHandle; + renderer->fencePool.availableFenceCount += 1; + + SDL_UnlockMutex(renderer->fencePool.lock); +} + +static void VULKAN_ReleaseFence( + Refresh_Renderer *driverData, + Refresh_Fence *fence +) { + VulkanFenceHandle *handle = (VulkanFenceHandle*) fence; + + if (SDL_AtomicDecRef(&handle->referenceCount)) + { + VULKAN_INTERNAL_ReturnFenceToPool((VulkanRenderer*) driverData, handle); + } +} + +static WindowData* VULKAN_INTERNAL_FetchWindowData( + SDL_Window *window +) { + return (WindowData*) SDL_GetWindowData(window, WINDOW_PROPERTY_DATA); +} + +static SDL_bool VULKAN_SupportsSwapchainComposition( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window); + VkSurfaceKHR surface; + SwapchainSupportDetails supportDetails; + SDL_bool destroySurface = SDL_FALSE; + SDL_bool result = SDL_FALSE; + Uint32 i; + + if (windowData == NULL || windowData->swapchainData == NULL) + { + /* Create a dummy surface is the window is not claimed */ + destroySurface = SDL_TRUE; + if (!SDL_Vulkan_CreateSurface( + window, + renderer->instance, + &surface + )) { + return SDL_FALSE; + } + } + else + { + surface = windowData->swapchainData->surface; + } + + VULKAN_INTERNAL_QuerySwapchainSupport( + renderer, + renderer->physicalDevice, + surface, + &supportDetails + ); + + for (i = 0; i < supportDetails.formatsLength; i += 1) + { + if ( supportDetails.formats[i].format == SwapchainCompositionToFormat[swapchainComposition] && + supportDetails.formats[i].colorSpace == SwapchainCompositionToColorSpace[swapchainComposition] ) + { + result = SDL_TRUE; + break; + } + } + + SDL_free(supportDetails.formats); + SDL_free(supportDetails.presentModes); + + if (destroySurface) + { + renderer->vkDestroySurfaceKHR( + renderer->instance, + surface, + NULL + ); + } + + return result; +} + +static SDL_bool VULKAN_SupportsPresentMode( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_PresentMode presentMode +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window); + VkSurfaceKHR surface; + SwapchainSupportDetails supportDetails; + SDL_bool destroySurface = SDL_FALSE; + SDL_bool result = SDL_FALSE; + Uint32 i; + + if (windowData == NULL || windowData->swapchainData == NULL) + { + /* Create a dummy surface is the window is not claimed */ + destroySurface = SDL_TRUE; + if (!SDL_Vulkan_CreateSurface( + window, + renderer->instance, + &surface + )) { + return SDL_FALSE; + } + } + else + { + surface = windowData->swapchainData->surface; + } + + VULKAN_INTERNAL_QuerySwapchainSupport( + renderer, + renderer->physicalDevice, + surface, + &supportDetails + ); + + for (i = 0; i < supportDetails.presentModesLength; i += 1) + { + if (supportDetails.presentModes[i] == SDLToVK_PresentMode[presentMode]) + { + result = SDL_TRUE; + break; + } + } + + SDL_free(supportDetails.formats); + SDL_free(supportDetails.presentModes); + + if (destroySurface) + { + renderer->vkDestroySurfaceKHR( + renderer->instance, + surface, + NULL + ); + } + + return result; +} + +static SDL_bool VULKAN_ClaimWindow( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition, + Refresh_PresentMode presentMode +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window); + + if (windowData == NULL) + { + windowData = SDL_malloc(sizeof(WindowData)); + windowData->window = window; + windowData->presentMode = presentMode; + windowData->swapchainComposition = swapchainComposition; + + if (VULKAN_INTERNAL_CreateSwapchain(renderer, windowData)) + { + SDL_SetWindowData(window, WINDOW_PROPERTY_DATA, windowData); + + if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) + { + renderer->claimedWindowCapacity *= 2; + renderer->claimedWindows = SDL_realloc( + renderer->claimedWindows, + renderer->claimedWindowCapacity * sizeof(WindowData*) + ); + } + + renderer->claimedWindows[renderer->claimedWindowCount] = windowData; + renderer->claimedWindowCount += 1; + + return 1; + } + else + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not create swapchain, failed to claim window!"); + SDL_free(windowData); + return 0; + } + } + else + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Window already claimed!"); + return 0; + } +} + +static void VULKAN_UnclaimWindow( + Refresh_Renderer *driverData, + SDL_Window *window +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window); + Uint32 i; + + if (windowData == NULL) + { + return; + } + + if (windowData->swapchainData != NULL) + { + VULKAN_Wait(driverData); + + for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) + { + if (windowData->swapchainData->inFlightFences[i] != NULL) + { + VULKAN_ReleaseFence( + driverData, + (Refresh_Fence*) windowData->swapchainData->inFlightFences[i] + ); + } + } + + VULKAN_INTERNAL_DestroySwapchain( + (VulkanRenderer*) driverData, + windowData + ); + } + + for (i = 0; i < renderer->claimedWindowCount; i += 1) + { + if (renderer->claimedWindows[i]->window == window) + { + renderer->claimedWindows[i] = renderer->claimedWindows[renderer->claimedWindowCount - 1]; + renderer->claimedWindowCount -= 1; + break; + } + } + + SDL_free(windowData); + SDL_SetWindowData(window, WINDOW_PROPERTY_DATA, NULL); +} + +static void VULKAN_INTERNAL_RecreateSwapchain( + VulkanRenderer* renderer, + WindowData *windowData +) { + Uint32 i; + + if (windowData->swapchainData != NULL) + { + VULKAN_Wait((Refresh_Renderer *)renderer); + + for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) + { + if (windowData->swapchainData->inFlightFences[i] != NULL) + { + VULKAN_ReleaseFence( + (Refresh_Renderer*) renderer, + (Refresh_Fence*) windowData->swapchainData->inFlightFences[i] + ); + } + } + } + + VULKAN_INTERNAL_DestroySwapchain(renderer, windowData); + VULKAN_INTERNAL_CreateSwapchain(renderer, windowData); +} + +static Refresh_Texture* VULKAN_AcquireSwapchainTexture( + Refresh_CommandBuffer *commandBuffer, + SDL_Window *window, + Uint32 *pWidth, + Uint32 *pHeight +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + Uint32 swapchainImageIndex; + WindowData *windowData; + VulkanSwapchainData *swapchainData; + VkResult acquireResult = VK_SUCCESS; + VulkanTextureContainer *swapchainTextureContainer = NULL; + VulkanPresentData *presentData; + + windowData = VULKAN_INTERNAL_FetchWindowData(window); + if (windowData == NULL) + { + return NULL; + } + + swapchainData = windowData->swapchainData; + + /* Window is claimed but swapchain is invalid! */ + if (swapchainData == NULL) + { + if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) + { + /* Window is minimized, don't bother */ + return NULL; + } + + /* Let's try to recreate */ + VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData); + swapchainData = windowData->swapchainData; + + if (swapchainData == NULL) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to recreate swapchain!"); + return NULL; + } + } + + if (swapchainData->inFlightFences[swapchainData->frameCounter] != NULL) + { + if (swapchainData->presentMode == VK_PRESENT_MODE_FIFO_KHR) + { + /* In VSYNC mode, block until the least recent presented frame is done */ + VULKAN_WaitForFences( + (Refresh_Renderer*) renderer, + SDL_TRUE, + 1, + (Refresh_Fence**) &swapchainData->inFlightFences[swapchainData->frameCounter] + ); + } + else + { + if (!VULKAN_QueryFence( + (Refresh_Renderer*) renderer, + (Refresh_Fence*) swapchainData->inFlightFences[swapchainData->frameCounter] + )) { + /* + * In MAILBOX or IMMEDIATE mode, if the least recent fence is not signaled, + * return NULL to indicate that rendering should be skipped + */ + return NULL; + } + } + + VULKAN_ReleaseFence( + (Refresh_Renderer*) renderer, + (Refresh_Fence*) swapchainData->inFlightFences[swapchainData->frameCounter] + ); + + swapchainData->inFlightFences[swapchainData->frameCounter] = NULL; + } + + acquireResult = renderer->vkAcquireNextImageKHR( + renderer->logicalDevice, + swapchainData->swapchain, + UINT64_MAX, + swapchainData->imageAvailableSemaphore[swapchainData->frameCounter], + VK_NULL_HANDLE, + &swapchainImageIndex + ); + + /* Acquisition is invalid, let's try to recreate */ + if (acquireResult != VK_SUCCESS && acquireResult != VK_SUBOPTIMAL_KHR) + { + VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData); + swapchainData = windowData->swapchainData; + + if (swapchainData == NULL) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to recreate swapchain!"); + return NULL; + } + + acquireResult = renderer->vkAcquireNextImageKHR( + renderer->logicalDevice, + swapchainData->swapchain, + UINT64_MAX, + swapchainData->imageAvailableSemaphore[swapchainData->frameCounter], + VK_NULL_HANDLE, + &swapchainImageIndex + ); + + if (acquireResult != VK_SUCCESS && acquireResult != VK_SUBOPTIMAL_KHR) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to acquire swapchain texture!"); + return NULL; + } + } + + swapchainTextureContainer = &swapchainData->textureContainers[swapchainImageIndex]; + + /* We need a special execution dependency with pWaitDstStageMask or image transition can start before acquire finishes */ + + VkImageMemoryBarrier imageBarrier; + imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageBarrier.pNext = NULL; + imageBarrier.srcAccessMask = 0; + imageBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageBarrier.image = swapchainTextureContainer->activeTextureHandle->vulkanTexture->image; + imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageBarrier.subresourceRange.baseMipLevel = 0; + imageBarrier.subresourceRange.levelCount = 1; + imageBarrier.subresourceRange.baseArrayLayer = 0; + imageBarrier.subresourceRange.layerCount = 1; + + renderer->vkCmdPipelineBarrier( + vulkanCommandBuffer->commandBuffer, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + 0, + 0, + NULL, + 0, + NULL, + 1, + &imageBarrier + ); + + /* Set up present struct */ + + if (vulkanCommandBuffer->presentDataCount == vulkanCommandBuffer->presentDataCapacity) + { + vulkanCommandBuffer->presentDataCapacity += 1; + vulkanCommandBuffer->presentDatas = SDL_realloc( + vulkanCommandBuffer->presentDatas, + vulkanCommandBuffer->presentDataCapacity * sizeof(VkPresentInfoKHR) + ); + } + + presentData = &vulkanCommandBuffer->presentDatas[vulkanCommandBuffer->presentDataCount]; + vulkanCommandBuffer->presentDataCount += 1; + + presentData->windowData = windowData; + presentData->swapchainImageIndex = swapchainImageIndex; + + /* Set up present semaphores */ + + if (vulkanCommandBuffer->waitSemaphoreCount == vulkanCommandBuffer->waitSemaphoreCapacity) + { + vulkanCommandBuffer->waitSemaphoreCapacity += 1; + vulkanCommandBuffer->waitSemaphores = SDL_realloc( + vulkanCommandBuffer->waitSemaphores, + vulkanCommandBuffer->waitSemaphoreCapacity * sizeof(VkSemaphore) + ); + } + + vulkanCommandBuffer->waitSemaphores[vulkanCommandBuffer->waitSemaphoreCount] = + swapchainData->imageAvailableSemaphore[swapchainData->frameCounter]; + vulkanCommandBuffer->waitSemaphoreCount += 1; + + if (vulkanCommandBuffer->signalSemaphoreCount == vulkanCommandBuffer->signalSemaphoreCapacity) + { + vulkanCommandBuffer->signalSemaphoreCapacity += 1; + vulkanCommandBuffer->signalSemaphores = SDL_realloc( + vulkanCommandBuffer->signalSemaphores, + vulkanCommandBuffer->signalSemaphoreCapacity * sizeof(VkSemaphore) + ); + } + + vulkanCommandBuffer->signalSemaphores[vulkanCommandBuffer->signalSemaphoreCount] = + swapchainData->renderFinishedSemaphore[swapchainData->frameCounter]; + vulkanCommandBuffer->signalSemaphoreCount += 1; + + *pWidth = swapchainData->extent.width; + *pHeight = swapchainData->extent.height; + + return (Refresh_Texture*) swapchainTextureContainer; +} + +static Refresh_TextureFormat VULKAN_GetSwapchainTextureFormat( + Refresh_Renderer *driverData, + SDL_Window *window +) { + WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window); + + if (windowData == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Cannot get swapchain format, window has not been claimed!"); + return 0; + } + + if (windowData->swapchainData == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Cannot get swapchain format, swapchain is currently invalid!"); + return 0; + } + + switch (windowData->swapchainData->format) + { + case VK_FORMAT_R8G8B8A8_UNORM: + return REFRESH_TEXTUREFORMAT_R8G8B8A8; + + case VK_FORMAT_B8G8R8A8_UNORM: + return REFRESH_TEXTUREFORMAT_B8G8R8A8; + + case VK_FORMAT_B8G8R8A8_SRGB: + return REFRESH_TEXTUREFORMAT_B8G8R8A8_SRGB; + + case VK_FORMAT_R8G8B8A8_SRGB: + return REFRESH_TEXTUREFORMAT_R8G8B8A8_SRGB; + + case VK_FORMAT_R16G16B16A16_SFLOAT: + return REFRESH_TEXTUREFORMAT_R16G16B16A16_SFLOAT; + + case VK_FORMAT_A2R10G10B10_UNORM_PACK32: + return REFRESH_TEXTUREFORMAT_A2R10G10B10; + + case VK_FORMAT_A2B10G10R10_UNORM_PACK32: + return REFRESH_TEXTUREFORMAT_A2B10G10R10; + + default: + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized swapchain format!"); + return 0; + } +} + +static void VULKAN_SetSwapchainParameters( + Refresh_Renderer *driverData, + SDL_Window *window, + Refresh_SwapchainComposition swapchainComposition, + Refresh_PresentMode presentMode +) { + WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window); + + if (windowData == NULL) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot set present mode, window has not been claimed!"); + return; + } + + /* The window size may have changed, always update even if these params are the same */ + windowData->presentMode = presentMode; + windowData->swapchainComposition = swapchainComposition; + + VULKAN_INTERNAL_RecreateSwapchain( + (VulkanRenderer *)driverData, + windowData + ); +} + +/* Submission structure */ + +static VulkanFenceHandle* VULKAN_INTERNAL_AcquireFenceFromPool( + VulkanRenderer *renderer +) { + VulkanFenceHandle *handle; + VkFenceCreateInfo fenceCreateInfo; + VkFence fence; + VkResult vulkanResult; + + if (renderer->fencePool.availableFenceCount == 0) + { + /* Create fence */ + fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceCreateInfo.pNext = NULL; + fenceCreateInfo.flags = 0; + + vulkanResult = renderer->vkCreateFence( + renderer->logicalDevice, + &fenceCreateInfo, + NULL, + &fence + ); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkCreateFence", vulkanResult); + return NULL; + } + + handle = SDL_malloc(sizeof(VulkanFenceHandle)); + handle->fence = fence; + SDL_AtomicSet(&handle->referenceCount, 0); + return handle; + } + + SDL_LockMutex(renderer->fencePool.lock); + + handle = renderer->fencePool.availableFences[renderer->fencePool.availableFenceCount - 1]; + renderer->fencePool.availableFenceCount -= 1; + + vulkanResult = renderer->vkResetFences( + renderer->logicalDevice, + 1, + &handle->fence + ); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkResetFences", vulkanResult); + } + + SDL_UnlockMutex(renderer->fencePool.lock); + + return handle; +} + +static void VULKAN_INTERNAL_PerformPendingDestroys( + VulkanRenderer *renderer +) { + Sint32 i, sliceIndex; + Sint32 refCountTotal; + + SDL_LockMutex(renderer->disposeLock); + + for (i = renderer->texturesToDestroyCount - 1; i >= 0; i -= 1) + { + refCountTotal = 0; + for (sliceIndex = 0; sliceIndex < renderer->texturesToDestroy[i]->sliceCount; sliceIndex += 1) + { + refCountTotal += SDL_AtomicGet(&renderer->texturesToDestroy[i]->slices[sliceIndex].referenceCount); + } + + if (refCountTotal == 0) + { + VULKAN_INTERNAL_DestroyTexture( + renderer, + renderer->texturesToDestroy[i] + ); + + renderer->texturesToDestroy[i] = renderer->texturesToDestroy[renderer->texturesToDestroyCount - 1]; + renderer->texturesToDestroyCount -= 1; + } + } + + for (i = renderer->buffersToDestroyCount - 1; i >= 0; i -= 1) + { + if (SDL_AtomicGet(&renderer->buffersToDestroy[i]->referenceCount) == 0) + { + VULKAN_INTERNAL_DestroyBuffer( + renderer, + renderer->buffersToDestroy[i]); + + renderer->buffersToDestroy[i] = renderer->buffersToDestroy[renderer->buffersToDestroyCount - 1]; + renderer->buffersToDestroyCount -= 1; + } + } + + for (i = renderer->graphicsPipelinesToDestroyCount - 1; i >= 0; i -= 1) + { + if (SDL_AtomicGet(&renderer->graphicsPipelinesToDestroy[i]->referenceCount) == 0) + { + VULKAN_INTERNAL_DestroyGraphicsPipeline( + renderer, + renderer->graphicsPipelinesToDestroy[i] + ); + + renderer->graphicsPipelinesToDestroy[i] = renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount - 1]; + renderer->graphicsPipelinesToDestroyCount -= 1; + } + } + + for (i = renderer->computePipelinesToDestroyCount - 1; i >= 0; i -= 1) + { + if (SDL_AtomicGet(&renderer->computePipelinesToDestroy[i]->referenceCount) == 0) + { + VULKAN_INTERNAL_DestroyComputePipeline( + renderer, + renderer->computePipelinesToDestroy[i] + ); + + renderer->computePipelinesToDestroy[i] = renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount - 1]; + renderer->computePipelinesToDestroyCount -= 1 ; + } + } + + for (i = renderer->shadersToDestroyCount - 1; i >= 0; i -= 1) + { + if (SDL_AtomicGet(&renderer->shadersToDestroy[i]->referenceCount) == 0) + { + VULKAN_INTERNAL_DestroyShaderModule( + renderer, + renderer->shadersToDestroy[i] + ); + + renderer->shadersToDestroy[i] = renderer->shadersToDestroy[renderer->shadersToDestroyCount - 1]; + renderer->shadersToDestroyCount -= 1; + } + } + + for (i = renderer->samplersToDestroyCount - 1; i >= 0; i -= 1) + { + if (SDL_AtomicGet(&renderer->samplersToDestroy[i]->referenceCount) == 0) + { + VULKAN_INTERNAL_DestroySampler( + renderer, + renderer->samplersToDestroy[i] + ); + + renderer->samplersToDestroy[i] = renderer->samplersToDestroy[renderer->samplersToDestroyCount - 1]; + renderer->samplersToDestroyCount -= 1; + } + } + + for (i = renderer->framebuffersToDestroyCount - 1; i >= 0; i -= 1) + { + if (SDL_AtomicGet(&renderer->framebuffersToDestroy[i]->referenceCount) == 0) + { + VULKAN_INTERNAL_DestroyFramebuffer( + renderer, + renderer->framebuffersToDestroy[i] + ); + + renderer->framebuffersToDestroy[i] = renderer->framebuffersToDestroy[renderer->framebuffersToDestroyCount - 1]; + renderer->framebuffersToDestroyCount -= 1; + } + } + + SDL_UnlockMutex(renderer->disposeLock); +} + +static void VULKAN_INTERNAL_CleanCommandBuffer( + VulkanRenderer *renderer, + VulkanCommandBuffer *commandBuffer +) { + Uint32 i; + DescriptorSetData *descriptorSetData; + + if (commandBuffer->autoReleaseFence) + { + VULKAN_ReleaseFence( + (Refresh_Renderer*) renderer, + (Refresh_Fence*) commandBuffer->inFlightFence + ); + + commandBuffer->inFlightFence = NULL; + } + + /* Bound descriptor sets are now available */ + + for (i = 0; i < commandBuffer->boundDescriptorSetDataCount; i += 1) + { + descriptorSetData = &commandBuffer->boundDescriptorSetDatas[i]; + + SDL_LockMutex(descriptorSetData->descriptorSetPool->lock); + + if (descriptorSetData->descriptorSetPool->inactiveDescriptorSetCount == descriptorSetData->descriptorSetPool->inactiveDescriptorSetCapacity) + { + descriptorSetData->descriptorSetPool->inactiveDescriptorSetCapacity *= 2; + descriptorSetData->descriptorSetPool->inactiveDescriptorSets = SDL_realloc( + descriptorSetData->descriptorSetPool->inactiveDescriptorSets, + descriptorSetData->descriptorSetPool->inactiveDescriptorSetCapacity * sizeof(VkDescriptorSet) + ); + } + + descriptorSetData->descriptorSetPool->inactiveDescriptorSets[descriptorSetData->descriptorSetPool->inactiveDescriptorSetCount] = descriptorSetData->descriptorSet; + descriptorSetData->descriptorSetPool->inactiveDescriptorSetCount += 1; + + SDL_UnlockMutex(descriptorSetData->descriptorSetPool->lock); + } + + commandBuffer->boundDescriptorSetDataCount = 0; + + /* Decrement reference counts */ + + for (i = 0; i < commandBuffer->usedBufferCount; i += 1) + { + (void)SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->referenceCount); + } + commandBuffer->usedBufferCount = 0; + + for (i = 0; i < commandBuffer->usedTextureSliceCount; i += 1) + { + (void)SDL_AtomicDecRef(&commandBuffer->usedTextureSlices[i]->referenceCount); + } + commandBuffer->usedTextureSliceCount = 0; + + for (i = 0; i < commandBuffer->usedSamplerCount; i += 1) + { + (void)SDL_AtomicDecRef(&commandBuffer->usedSamplers[i]->referenceCount); + } + commandBuffer->usedSamplerCount = 0; + + for (i = 0; i < commandBuffer->usedGraphicsPipelineCount; i += 1) + { + (void)SDL_AtomicDecRef(&commandBuffer->usedGraphicsPipelines[i]->referenceCount); + } + commandBuffer->usedGraphicsPipelineCount = 0; + + for (i = 0; i < commandBuffer->usedComputePipelineCount; i += 1) + { + (void)SDL_AtomicDecRef(&commandBuffer->usedComputePipelines[i]->referenceCount); + } + commandBuffer->usedComputePipelineCount = 0; + + for (i = 0; i < commandBuffer->usedFramebufferCount; i += 1) + { + (void)SDL_AtomicDecRef(&commandBuffer->usedFramebuffers[i]->referenceCount); + } + commandBuffer->usedFramebufferCount = 0; + + /* Reset presentation data */ + + commandBuffer->presentDataCount = 0; + commandBuffer->waitSemaphoreCount = 0; + commandBuffer->signalSemaphoreCount = 0; + + /* Reset defrag state */ + + if (commandBuffer->isDefrag) + { + renderer->defragInProgress = 0; + } + + /* Return command buffer to pool */ + + SDL_LockMutex(renderer->acquireCommandBufferLock); + + if (commandBuffer->commandPool->inactiveCommandBufferCount == commandBuffer->commandPool->inactiveCommandBufferCapacity) + { + commandBuffer->commandPool->inactiveCommandBufferCapacity += 1; + commandBuffer->commandPool->inactiveCommandBuffers = SDL_realloc( + commandBuffer->commandPool->inactiveCommandBuffers, + commandBuffer->commandPool->inactiveCommandBufferCapacity * sizeof(VulkanCommandBuffer*) + ); + } + + commandBuffer->commandPool->inactiveCommandBuffers[ + commandBuffer->commandPool->inactiveCommandBufferCount + ] = commandBuffer; + commandBuffer->commandPool->inactiveCommandBufferCount += 1; + + SDL_UnlockMutex(renderer->acquireCommandBufferLock); + + /* Remove this command buffer from the submitted list */ + for (i = 0; i < renderer->submittedCommandBufferCount; i += 1) + { + if (renderer->submittedCommandBuffers[i] == commandBuffer) + { + renderer->submittedCommandBuffers[i] = renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount - 1]; + renderer->submittedCommandBufferCount -= 1; + } + } +} + +static void VULKAN_WaitForFences( + Refresh_Renderer *driverData, + SDL_bool waitAll, + Uint32 fenceCount, + Refresh_Fence **pFences +) { + VulkanRenderer* renderer = (VulkanRenderer*) driverData; + VkFence* fences = SDL_stack_alloc(VkFence, fenceCount); + VkResult result; + Sint32 i; + + for (i = 0; i < fenceCount; i += 1) + { + fences[i] = ((VulkanFenceHandle*) pFences[i])->fence; + } + + result = renderer->vkWaitForFences( + renderer->logicalDevice, + fenceCount, + fences, + waitAll, + UINT64_MAX + ); + + if (result != VK_SUCCESS) + { + LogVulkanResultAsError("vkWaitForFences", result); + } + + SDL_stack_free(fences); + + SDL_LockMutex(renderer->submitLock); + + for (i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) + { + result = renderer->vkGetFenceStatus( + renderer->logicalDevice, + renderer->submittedCommandBuffers[i]->inFlightFence->fence + ); + + if (result == VK_SUCCESS) + { + VULKAN_INTERNAL_CleanCommandBuffer( + renderer, + renderer->submittedCommandBuffers[i] + ); + } + } + + VULKAN_INTERNAL_PerformPendingDestroys(renderer); + + SDL_UnlockMutex(renderer->submitLock); +} + +static void VULKAN_Wait( + Refresh_Renderer *driverData +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanCommandBuffer *commandBuffer; + VkResult result; + Sint32 i; + + result = renderer->vkDeviceWaitIdle(renderer->logicalDevice); + + if (result != VK_SUCCESS) + { + LogVulkanResultAsError("vkDeviceWaitIdle", result); + return; + } + + SDL_LockMutex(renderer->submitLock); + + for (i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) + { + commandBuffer = renderer->submittedCommandBuffers[i]; + VULKAN_INTERNAL_CleanCommandBuffer(renderer, commandBuffer); + } + + VULKAN_INTERNAL_PerformPendingDestroys(renderer); + + SDL_UnlockMutex(renderer->submitLock); +} + +static Refresh_Fence* VULKAN_SubmitAndAcquireFence( + Refresh_CommandBuffer *commandBuffer +) { + VulkanCommandBuffer *vulkanCommandBuffer; + + vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + vulkanCommandBuffer->autoReleaseFence = 0; + + VULKAN_Submit(commandBuffer); + + return (Refresh_Fence*) vulkanCommandBuffer->inFlightFence; +} + +static void VULKAN_Submit( + Refresh_CommandBuffer *commandBuffer +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VkSubmitInfo submitInfo; + VkPresentInfoKHR presentInfo; + VulkanPresentData *presentData; + VkResult vulkanResult, presentResult = VK_SUCCESS; + VkPipelineStageFlags waitStages[MAX_PRESENT_COUNT]; + Uint32 swapchainImageIndex; + VulkanTextureSlice *swapchainTextureSlice; + Uint8 commandBufferCleaned = 0; + VulkanMemorySubAllocator *allocator; + SDL_bool presenting = SDL_FALSE; + Sint32 i, j; + + SDL_LockMutex(renderer->submitLock); + + /* FIXME: Can this just be permanent? */ + for (i = 0; i < MAX_PRESENT_COUNT; i += 1) + { + waitStages[i] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } + + for (j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) + { + swapchainImageIndex = vulkanCommandBuffer->presentDatas[j].swapchainImageIndex; + swapchainTextureSlice = VULKAN_INTERNAL_FetchTextureSlice( + vulkanCommandBuffer->presentDatas[j].windowData->swapchainData->textureContainers[swapchainImageIndex].activeTextureHandle->vulkanTexture, + 0, + 0 + ); + + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + vulkanCommandBuffer, + VULKAN_TEXTURE_USAGE_MODE_PRESENT, + swapchainTextureSlice + ); + } + + VULKAN_INTERNAL_EndCommandBuffer(renderer, vulkanCommandBuffer); + + vulkanCommandBuffer->inFlightFence = VULKAN_INTERNAL_AcquireFenceFromPool(renderer); + + /* Command buffer has a reference to the in-flight fence */ + (void)SDL_AtomicIncRef(&vulkanCommandBuffer->inFlightFence->referenceCount); + + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.pNext = NULL; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &vulkanCommandBuffer->commandBuffer; + + submitInfo.pWaitDstStageMask = waitStages; + submitInfo.pWaitSemaphores = vulkanCommandBuffer->waitSemaphores; + submitInfo.waitSemaphoreCount = vulkanCommandBuffer->waitSemaphoreCount; + submitInfo.pSignalSemaphores = vulkanCommandBuffer->signalSemaphores; + submitInfo.signalSemaphoreCount = vulkanCommandBuffer->signalSemaphoreCount; + + vulkanResult = renderer->vkQueueSubmit( + renderer->unifiedQueue, + 1, + &submitInfo, + vulkanCommandBuffer->inFlightFence->fence + ); + + if (vulkanResult != VK_SUCCESS) + { + LogVulkanResultAsError("vkQueueSubmit", vulkanResult); + } + + /* Mark command buffers as submitted */ + + if (renderer->submittedCommandBufferCount + 1 >= renderer->submittedCommandBufferCapacity) + { + renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1; + + renderer->submittedCommandBuffers = SDL_realloc( + renderer->submittedCommandBuffers, + sizeof(VulkanCommandBuffer*) * renderer->submittedCommandBufferCapacity + ); + } + + renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = vulkanCommandBuffer; + renderer->submittedCommandBufferCount += 1; + + /* Present, if applicable */ + + for (j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) + { + presenting = SDL_TRUE; + + presentData = &vulkanCommandBuffer->presentDatas[j]; + + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.pNext = NULL; + presentInfo.pWaitSemaphores = + &presentData->windowData->swapchainData->renderFinishedSemaphore[ + presentData->windowData->swapchainData->frameCounter + ]; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pSwapchains = &presentData->windowData->swapchainData->swapchain; + presentInfo.swapchainCount = 1; + presentInfo.pImageIndices = &presentData->swapchainImageIndex; + presentInfo.pResults = NULL; + + presentResult = renderer->vkQueuePresentKHR( + renderer->unifiedQueue, + &presentInfo + ); + + presentData->windowData->swapchainData->frameCounter = + (presentData->windowData->swapchainData->frameCounter + 1) % MAX_FRAMES_IN_FLIGHT; + + if (presentResult != VK_SUCCESS) + { + VULKAN_INTERNAL_RecreateSwapchain( + renderer, + presentData->windowData + ); + } + else + { + /* If presenting, the swapchain is using the in-flight fence */ + presentData->windowData->swapchainData->inFlightFences[ + presentData->windowData->swapchainData->frameCounter + ] = vulkanCommandBuffer->inFlightFence; + + (void)SDL_AtomicIncRef(&vulkanCommandBuffer->inFlightFence->referenceCount); + } + } + + /* Check if we can perform any cleanups */ + + for (i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) + { + vulkanResult = renderer->vkGetFenceStatus( + renderer->logicalDevice, + renderer->submittedCommandBuffers[i]->inFlightFence->fence + ); + + if (vulkanResult == VK_SUCCESS) + { + VULKAN_INTERNAL_CleanCommandBuffer( + renderer, + renderer->submittedCommandBuffers[i] + ); + + commandBufferCleaned = 1; + } + } + + if (commandBufferCleaned) + { + SDL_LockMutex(renderer->allocatorLock); + + for (i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) + { + allocator = &renderer->memoryAllocator->subAllocators[i]; + + for (j = allocator->allocationCount - 1; j >= 0; j -= 1) + { + if (allocator->allocations[j]->usedRegionCount == 0) + { + VULKAN_INTERNAL_DeallocateMemory( + renderer, + allocator, + j + ); + } + } + } + + SDL_UnlockMutex(renderer->allocatorLock); + } + + /* Check pending destroys */ + VULKAN_INTERNAL_PerformPendingDestroys(renderer); + + /* Defrag! */ + if ( + presenting && + renderer->allocationsToDefragCount > 0 && + !renderer->defragInProgress + ) { + VULKAN_INTERNAL_DefragmentMemory(renderer); + } + + SDL_UnlockMutex(renderer->submitLock); +} + +static Uint8 VULKAN_INTERNAL_DefragmentMemory( + VulkanRenderer *renderer +) { + VulkanMemoryAllocation *allocation; + VulkanMemoryUsedRegion *currentRegion; + VulkanBuffer* newBuffer; + VulkanTexture* newTexture; + VkBufferCopy bufferCopy; + VkImageCopy imageCopy; + VulkanCommandBuffer *commandBuffer; + VulkanTextureSlice *srcSlice; + VulkanTextureSlice *dstSlice; + Uint32 i, sliceIndex; + + SDL_LockMutex(renderer->allocatorLock); + + renderer->defragInProgress = 1; + + commandBuffer = (VulkanCommandBuffer*) VULKAN_AcquireCommandBuffer((Refresh_Renderer *) renderer); + commandBuffer->isDefrag = 1; + + allocation = renderer->allocationsToDefrag[renderer->allocationsToDefragCount - 1]; + renderer->allocationsToDefragCount -= 1; + + /* For each used region in the allocation + * create a new resource, copy the data + * and re-point the resource containers + */ + for (i = 0; i < allocation->usedRegionCount; i += 1) + { + currentRegion = allocation->usedRegions[i]; + + if (currentRegion->isBuffer && !currentRegion->vulkanBuffer->markedForDestroy) + { + currentRegion->vulkanBuffer->usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; + + newBuffer = VULKAN_INTERNAL_CreateBuffer( + renderer, + currentRegion->vulkanBuffer->size, + currentRegion->vulkanBuffer->usageFlags, + currentRegion->vulkanBuffer->type + ); + + if (newBuffer == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create defrag buffer!"); + return 0; + } + + if ( + renderer->debugMode && + renderer->supportsDebugUtils && + currentRegion->vulkanBuffer->handle != NULL && + currentRegion->vulkanBuffer->handle->container != NULL && + currentRegion->vulkanBuffer->handle->container->debugName != NULL + ) { + VULKAN_INTERNAL_SetBufferName( + renderer, + newBuffer, + currentRegion->vulkanBuffer->handle->container->debugName + ); + } + + /* Copy buffer contents if necessary */ + if ( + currentRegion->vulkanBuffer->type == VULKAN_BUFFER_TYPE_GPU && currentRegion->vulkanBuffer->transitioned + ) { + VULKAN_INTERNAL_BufferTransitionFromDefaultUsage( + renderer, + commandBuffer, + VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE, + currentRegion->vulkanBuffer + ); + + VULKAN_INTERNAL_BufferTransitionFromDefaultUsage( + renderer, + commandBuffer, + VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION, + newBuffer + ); + + bufferCopy.srcOffset = 0; + bufferCopy.dstOffset = 0; + bufferCopy.size = currentRegion->resourceSize; + + renderer->vkCmdCopyBuffer( + commandBuffer->commandBuffer, + currentRegion->vulkanBuffer->buffer, + newBuffer->buffer, + 1, + &bufferCopy + ); + + VULKAN_INTERNAL_BufferTransitionToDefaultUsage( + renderer, + commandBuffer, + VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION, + newBuffer + ); + + VULKAN_INTERNAL_TrackBuffer(renderer, commandBuffer, currentRegion->vulkanBuffer); + VULKAN_INTERNAL_TrackBuffer(renderer, commandBuffer, newBuffer); + } + + /* re-point original container to new buffer */ + if (currentRegion->vulkanBuffer->handle != NULL) + { + newBuffer->handle = currentRegion->vulkanBuffer->handle; + newBuffer->handle->vulkanBuffer = newBuffer; + currentRegion->vulkanBuffer->handle = NULL; + } + + VULKAN_INTERNAL_ReleaseBuffer(renderer, currentRegion->vulkanBuffer); + } + else if (!currentRegion->isBuffer && !currentRegion->vulkanTexture->markedForDestroy) + { + newTexture = VULKAN_INTERNAL_CreateTexture( + renderer, + currentRegion->vulkanTexture->dimensions.width, + currentRegion->vulkanTexture->dimensions.height, + currentRegion->vulkanTexture->depth, + currentRegion->vulkanTexture->isCube, + currentRegion->vulkanTexture->layerCount, + currentRegion->vulkanTexture->levelCount, + currentRegion->vulkanTexture->sampleCount, + currentRegion->vulkanTexture->format, + currentRegion->vulkanTexture->swizzle, + currentRegion->vulkanTexture->aspectFlags, + currentRegion->vulkanTexture->usageFlags + ); + + if (newTexture == NULL) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create defrag texture!"); + return 0; + } + + for (sliceIndex = 0; sliceIndex < currentRegion->vulkanTexture->sliceCount; sliceIndex += 1) + { + /* copy slice if necessary */ + srcSlice = ¤tRegion->vulkanTexture->slices[sliceIndex]; + dstSlice = &newTexture->slices[sliceIndex]; + + /* Set debug name if it exists */ + if ( + renderer->debugMode && + renderer->supportsDebugUtils && + srcSlice->parent->handle != NULL && + srcSlice->parent->handle->container != NULL && + srcSlice->parent->handle->container->debugName != NULL + ) { + VULKAN_INTERNAL_SetTextureName( + renderer, + currentRegion->vulkanTexture, + srcSlice->parent->handle->container->debugName + ); + } + + if (srcSlice->transitioned) + { + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + commandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE, + srcSlice + ); + + VULKAN_INTERNAL_TextureTransitionFromDefaultUsage( + renderer, + commandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, + dstSlice + ); + + imageCopy.srcOffset.x = 0; + imageCopy.srcOffset.y = 0; + imageCopy.srcOffset.z = 0; + imageCopy.srcSubresource.aspectMask = srcSlice->parent->aspectFlags; + imageCopy.srcSubresource.baseArrayLayer = srcSlice->layer; + imageCopy.srcSubresource.layerCount = 1; + imageCopy.srcSubresource.mipLevel = srcSlice->level; + imageCopy.extent.width = SDL_max(1, srcSlice->parent->dimensions.width >> srcSlice->level); + imageCopy.extent.height = SDL_max(1, srcSlice->parent->dimensions.height >> srcSlice->level); + imageCopy.extent.depth = srcSlice->parent->depth; + imageCopy.dstOffset.x = 0; + imageCopy.dstOffset.y = 0; + imageCopy.dstOffset.z = 0; + imageCopy.dstSubresource.aspectMask = dstSlice->parent->aspectFlags; + imageCopy.dstSubresource.baseArrayLayer = dstSlice->layer; + imageCopy.dstSubresource.layerCount = 1; + imageCopy.dstSubresource.mipLevel = dstSlice->level; + + renderer->vkCmdCopyImage( + commandBuffer->commandBuffer, + currentRegion->vulkanTexture->image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + newTexture->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &imageCopy + ); + + VULKAN_INTERNAL_TextureTransitionToDefaultUsage( + renderer, + commandBuffer, + VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION, + dstSlice + ); + + VULKAN_INTERNAL_TrackTextureSlice(renderer, commandBuffer, srcSlice); + VULKAN_INTERNAL_TrackTextureSlice(renderer, commandBuffer, dstSlice); + } + } + + /* re-point original container to new texture */ + newTexture->handle = currentRegion->vulkanTexture->handle; + newTexture->handle->vulkanTexture = newTexture; + currentRegion->vulkanTexture->handle = NULL; + + VULKAN_INTERNAL_ReleaseTexture(renderer, currentRegion->vulkanTexture); + } + } + + SDL_UnlockMutex(renderer->allocatorLock); + + VULKAN_Submit( + (Refresh_CommandBuffer*) commandBuffer + ); + + return 1; +} + +/* Queries */ + +static Refresh_OcclusionQuery* VULKAN_CreateOcclusionQuery( + Refresh_Renderer *driverData +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanOcclusionQuery *query = (VulkanOcclusionQuery*) SDL_malloc(sizeof(VulkanOcclusionQuery)); + + SDL_LockMutex(renderer->queryLock); + + if (renderer->freeQueryIndexStackHead == -1) + { + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "Query limit of %d has been exceeded!", + MAX_QUERIES + ); + return NULL; + } + + query->index = (Uint32) renderer->freeQueryIndexStackHead; + renderer->freeQueryIndexStackHead = renderer->freeQueryIndexStack[renderer->freeQueryIndexStackHead]; + + SDL_UnlockMutex(renderer->queryLock); + + return (Refresh_OcclusionQuery*) query; +} + +static void VULKAN_OcclusionQueryBegin( + Refresh_CommandBuffer *commandBuffer, + Refresh_OcclusionQuery *query +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanOcclusionQuery *vulkanQuery = (VulkanOcclusionQuery*) query; + + renderer->vkCmdResetQueryPool( + vulkanCommandBuffer->commandBuffer, + renderer->queryPool, + vulkanQuery->index, + 1 + ); + + renderer->vkCmdBeginQuery( + vulkanCommandBuffer->commandBuffer, + renderer->queryPool, + vulkanQuery->index, + renderer->supportsPreciseOcclusionQueries ? + VK_QUERY_CONTROL_PRECISE_BIT : + 0 + ); +} + +static void VULKAN_OcclusionQueryEnd( + Refresh_CommandBuffer *commandBuffer, + Refresh_OcclusionQuery *query +) { + VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer*) commandBuffer; + VulkanRenderer *renderer = (VulkanRenderer*) vulkanCommandBuffer->renderer; + VulkanOcclusionQuery *vulkanQuery = (VulkanOcclusionQuery*) query; + + renderer->vkCmdEndQuery( + vulkanCommandBuffer->commandBuffer, + renderer->queryPool, + vulkanQuery->index + ); +} + +static SDL_bool VULKAN_OcclusionQueryPixelCount( + Refresh_Renderer *driverData, + Refresh_OcclusionQuery *query, + Uint32 *pixelCount +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VulkanOcclusionQuery *vulkanQuery = (VulkanOcclusionQuery*) query; + VkResult vulkanResult; + Uint32 queryResult; + + SDL_LockMutex(renderer->queryLock); + vulkanResult = renderer->vkGetQueryPoolResults( + renderer->logicalDevice, + renderer->queryPool, + vulkanQuery->index, + 1, + sizeof(queryResult), + &queryResult, + 0, + 0 + ); + SDL_UnlockMutex(renderer->queryLock); + + *pixelCount = queryResult; + return vulkanResult == VK_SUCCESS; +} + +/* Format Info */ + +static SDL_bool VULKAN_IsTextureFormatSupported( + Refresh_Renderer *driverData, + Refresh_TextureFormat format, + Refresh_TextureType type, + Refresh_TextureUsageFlags usage +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + VkFormat vulkanFormat = SDLToVK_SurfaceFormat[format]; + VkImageUsageFlags vulkanUsage = 0; + VkImageCreateFlags createFlags = 0; + VkImageFormatProperties properties; + VkResult vulkanResult; + + if (usage & REFRESH_TEXTUREUSAGE_SAMPLER_BIT) + { + vulkanUsage |= VK_IMAGE_USAGE_SAMPLED_BIT; + } + if (usage & REFRESH_TEXTUREUSAGE_COLOR_TARGET_BIT) + { + vulkanUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + if (usage & REFRESH_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT) + { + vulkanUsage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + if (usage & ( + REFRESH_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT | + REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT | + REFRESH_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT + )) { + vulkanUsage |= VK_IMAGE_USAGE_STORAGE_BIT; + } + + if (type == REFRESH_TEXTURETYPE_CUBE) + { + createFlags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + } + + vulkanResult = renderer->vkGetPhysicalDeviceImageFormatProperties( + renderer->physicalDevice, + vulkanFormat, + (type == REFRESH_TEXTURETYPE_3D) ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D, + VK_IMAGE_TILING_OPTIMAL, + vulkanUsage, + createFlags, + &properties + ); + + return vulkanResult == VK_SUCCESS; +} + +static Refresh_SampleCount VULKAN_GetBestSampleCount( + Refresh_Renderer *driverData, + Refresh_TextureFormat format, + Refresh_SampleCount desiredSampleCount +) { + VulkanRenderer *renderer = (VulkanRenderer*) driverData; + Uint32 maxSupported; + VkSampleCountFlagBits bits = IsDepthFormat(format) ? + renderer->physicalDeviceProperties.properties.limits.framebufferDepthSampleCounts : + renderer->physicalDeviceProperties.properties.limits.framebufferColorSampleCounts; + + if (bits & VK_SAMPLE_COUNT_8_BIT) + { + maxSupported = REFRESH_SAMPLECOUNT_8; + } + else if (bits & VK_SAMPLE_COUNT_4_BIT) + { + maxSupported = REFRESH_SAMPLECOUNT_4; + } + else if (bits & VK_SAMPLE_COUNT_2_BIT) + { + maxSupported = REFRESH_SAMPLECOUNT_2; + } + else + { + maxSupported = REFRESH_SAMPLECOUNT_1; + } + + return (Refresh_SampleCount) SDL_min(maxSupported, desiredSampleCount); +} + +/* SPIR-V Cross Interop */ + +static Refresh_Shader* VULKAN_CompileFromSPIRVCross( + Refresh_Renderer *driverData, + Refresh_ShaderStage shader_stage, + const char *entryPointName, + const char *source +) { + SDL_assert(!"You should not have gotten here!!!"); + return NULL; +} + +/* Device instantiation */ + +static inline Uint8 CheckDeviceExtensions( + VkExtensionProperties *extensions, + Uint32 numExtensions, + VulkanExtensions *supports +) { + Uint32 i; + + SDL_memset(supports, '\0', sizeof(VulkanExtensions)); + for (i = 0; i < numExtensions; i += 1) + { + const char *name = extensions[i].extensionName; + #define CHECK(ext) \ + if (SDL_strcmp(name, "VK_" #ext) == 0) \ + { \ + supports->ext = 1; \ + } + CHECK(KHR_swapchain) + else CHECK(KHR_maintenance1) + else CHECK(KHR_get_memory_requirements2) + else CHECK(KHR_driver_properties) + else CHECK(EXT_vertex_attribute_divisor) + else CHECK(KHR_portability_subset) + #undef CHECK + } + + return ( supports->KHR_swapchain && + supports->KHR_maintenance1 && + supports->KHR_get_memory_requirements2 ); +} + +static inline Uint32 GetDeviceExtensionCount(VulkanExtensions *supports) +{ + return ( + supports->KHR_swapchain + + supports->KHR_maintenance1 + + supports->KHR_get_memory_requirements2 + + supports->KHR_driver_properties + + supports->EXT_vertex_attribute_divisor + + supports->KHR_portability_subset + ); +} + +static inline void CreateDeviceExtensionArray( + VulkanExtensions *supports, + const char **extensions +) { + Uint8 cur = 0; + #define CHECK(ext) \ + if (supports->ext) \ + { \ + extensions[cur++] = "VK_" #ext; \ + } + CHECK(KHR_swapchain) + CHECK(KHR_maintenance1) + CHECK(KHR_get_memory_requirements2) + CHECK(KHR_driver_properties) + CHECK(EXT_vertex_attribute_divisor) + CHECK(KHR_portability_subset) + #undef CHECK +} + +static inline Uint8 SupportsInstanceExtension( + const char *ext, + VkExtensionProperties *availableExtensions, + Uint32 numAvailableExtensions +) { + Uint32 i; + for (i = 0; i < numAvailableExtensions; i += 1) + { + if (SDL_strcmp(ext, availableExtensions[i].extensionName) == 0) + { + return 1; + } + } + return 0; +} + +static Uint8 VULKAN_INTERNAL_CheckInstanceExtensions( + const char **requiredExtensions, + Uint32 requiredExtensionsLength, + Uint8 *supportsDebugUtils +) { + Uint32 extensionCount, i; + VkExtensionProperties *availableExtensions; + Uint8 allExtensionsSupported = 1; + + vkEnumerateInstanceExtensionProperties( + NULL, + &extensionCount, + NULL + ); + availableExtensions = SDL_malloc( + extensionCount * sizeof(VkExtensionProperties) + ); + vkEnumerateInstanceExtensionProperties( + NULL, + &extensionCount, + availableExtensions + ); + + for (i = 0; i < requiredExtensionsLength; i += 1) + { + if (!SupportsInstanceExtension( + requiredExtensions[i], + availableExtensions, + extensionCount + )) { + allExtensionsSupported = 0; + break; + } + } + + /* This is optional, but nice to have! */ + *supportsDebugUtils = SupportsInstanceExtension( + VK_EXT_DEBUG_UTILS_EXTENSION_NAME, + availableExtensions, + extensionCount + ); + + SDL_free(availableExtensions); + return allExtensionsSupported; +} + +static Uint8 VULKAN_INTERNAL_CheckDeviceExtensions( + VulkanRenderer *renderer, + VkPhysicalDevice physicalDevice, + VulkanExtensions *physicalDeviceExtensions +) { + Uint32 extensionCount; + VkExtensionProperties *availableExtensions; + Uint8 allExtensionsSupported; + + renderer->vkEnumerateDeviceExtensionProperties( + physicalDevice, + NULL, + &extensionCount, + NULL + ); + availableExtensions = (VkExtensionProperties*) SDL_malloc( + extensionCount * sizeof(VkExtensionProperties) + ); + renderer->vkEnumerateDeviceExtensionProperties( + physicalDevice, + NULL, + &extensionCount, + availableExtensions + ); + + allExtensionsSupported = CheckDeviceExtensions( + availableExtensions, + extensionCount, + physicalDeviceExtensions + ); + + SDL_free(availableExtensions); + return allExtensionsSupported; +} + +static Uint8 VULKAN_INTERNAL_CheckValidationLayers( + const char** validationLayers, + Uint32 validationLayersLength +) { + Uint32 layerCount; + VkLayerProperties *availableLayers; + Uint32 i, j; + Uint8 layerFound = 0; + + vkEnumerateInstanceLayerProperties(&layerCount, NULL); + availableLayers = (VkLayerProperties*) SDL_malloc( + layerCount * sizeof(VkLayerProperties) + ); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers); + + for (i = 0; i < validationLayersLength; i += 1) + { + layerFound = 0; + + for (j = 0; j < layerCount; j += 1) + { + if (SDL_strcmp(validationLayers[i], availableLayers[j].layerName) == 0) + { + layerFound = 1; + break; + } + } + + if (!layerFound) + { + break; + } + } + + SDL_free(availableLayers); + return layerFound; +} + +static Uint8 VULKAN_INTERNAL_CreateInstance( + VulkanRenderer *renderer, + SDL_Window *deviceWindowHandle +) { + VkResult vulkanResult; + VkApplicationInfo appInfo; + VkInstanceCreateFlags createFlags; + const char **instanceExtensionNames; + Uint32 instanceExtensionCount; + VkInstanceCreateInfo createInfo; + static const char *layerNames[] = { "VK_LAYER_KHRONOS_validation" }; + + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pNext = NULL; + appInfo.pApplicationName = NULL; + appInfo.applicationVersion = 0; + appInfo.pEngineName = "SDLGPU"; + appInfo.engineVersion = ( (2 * 100 * 100) + (0 * 100) + (0) ); + appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0); + + createFlags = 0; + + if (!SDL_Vulkan_GetInstanceExtensions( + (SDL_Window*) deviceWindowHandle, + &instanceExtensionCount, + NULL + )) { + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "SDL_Vulkan_GetInstanceExtensions(): getExtensionCount: %s", + SDL_GetError() + ); + + return 0; + } + + /* Extra space for the following extensions: + * VK_KHR_get_physical_device_properties2 + * VK_EXT_swapchain_colorspace + * VK_EXT_debug_utils + * VK_KHR_portability_enumeration + */ + instanceExtensionNames = SDL_stack_alloc( + const char*, + instanceExtensionCount + 4 + ); + + if (!SDL_Vulkan_GetInstanceExtensions( + deviceWindowHandle, + &instanceExtensionCount, + instanceExtensionNames + )) { + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "SDL_Vulkan_GetInstanceExtensions(): %s", + SDL_GetError() + ); + } + + /* Core since 1.1 */ + instanceExtensionNames[instanceExtensionCount++] = + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; + + instanceExtensionNames[instanceExtensionCount++] = + VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME; + +#ifdef SDL_PLATFORM_APPLE + instanceExtensionNames[instanceExtensionCount++] = + VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME; + createFlags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; +#endif + + if (!VULKAN_INTERNAL_CheckInstanceExtensions( + instanceExtensionNames, + instanceExtensionCount, + &renderer->supportsDebugUtils + )) { + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "Required Vulkan instance extensions not supported" + ); + + SDL_stack_free((char*) instanceExtensionNames); + return 0; + } + + if (renderer->supportsDebugUtils) + { + /* Append the debug extension to the end */ + instanceExtensionNames[instanceExtensionCount++] = + VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + } + else + { + SDL_LogWarn( + SDL_LOG_CATEGORY_APPLICATION, + "%s is not supported!", + VK_EXT_DEBUG_UTILS_EXTENSION_NAME + ); + } + + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pNext = NULL; + createInfo.flags = createFlags; + createInfo.pApplicationInfo = &appInfo; + createInfo.ppEnabledLayerNames = layerNames; + createInfo.enabledExtensionCount = instanceExtensionCount; + createInfo.ppEnabledExtensionNames = instanceExtensionNames; + if (renderer->debugMode) + { + createInfo.enabledLayerCount = SDL_arraysize(layerNames); + if (!VULKAN_INTERNAL_CheckValidationLayers( + layerNames, + createInfo.enabledLayerCount + )) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Validation layers not found, continuing without validation"); + createInfo.enabledLayerCount = 0; + } + else + { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Validation layers enabled, expect debug level performance!"); + } + } + else + { + createInfo.enabledLayerCount = 0; + } + + vulkanResult = vkCreateInstance(&createInfo, NULL, &renderer->instance); + if (vulkanResult != VK_SUCCESS) + { + SDL_LogError( + SDL_LOG_CATEGORY_APPLICATION, + "vkCreateInstance failed: %s", + VkErrorMessages(vulkanResult) + ); + + SDL_stack_free((char*) instanceExtensionNames); + return 0; + } + + SDL_stack_free((char*) instanceExtensionNames); + return 1; +} + +static Uint8 VULKAN_INTERNAL_IsDeviceSuitable( + VulkanRenderer *renderer, + VkPhysicalDevice physicalDevice, + VulkanExtensions *physicalDeviceExtensions, + VkSurfaceKHR surface, + Uint32 *queueFamilyIndex, + Uint8 *deviceRank +) { + Uint32 queueFamilyCount, queueFamilyRank, queueFamilyBest; + SwapchainSupportDetails swapchainSupportDetails; + VkQueueFamilyProperties *queueProps; + VkBool32 supportsPresent; + Uint8 querySuccess; + VkPhysicalDeviceProperties deviceProperties; + Uint32 i; + + /* Get the device rank before doing any checks, in case one fails. + * Note: If no dedicated device exists, one that supports our features + * would be fine + */ + renderer->vkGetPhysicalDeviceProperties( + physicalDevice, + &deviceProperties + ); + if (*deviceRank < DEVICE_PRIORITY[deviceProperties.deviceType]) + { + /* This device outranks the best device we've found so far! + * This includes a dedicated GPU that has less features than an + * integrated GPU, because this is a freak case that is almost + * never intentionally desired by the end user + */ + *deviceRank = DEVICE_PRIORITY[deviceProperties.deviceType]; + } + else if (*deviceRank > DEVICE_PRIORITY[deviceProperties.deviceType]) + { + /* Device is outranked by a previous device, don't even try to + * run a query and reset the rank to avoid overwrites + */ + *deviceRank = 0; + return 0; + } + + if (!VULKAN_INTERNAL_CheckDeviceExtensions( + renderer, + physicalDevice, + physicalDeviceExtensions + )) { + return 0; + } + + renderer->vkGetPhysicalDeviceQueueFamilyProperties( + physicalDevice, + &queueFamilyCount, + NULL + ); + + queueProps = (VkQueueFamilyProperties*) SDL_stack_alloc( + VkQueueFamilyProperties, + queueFamilyCount + ); + renderer->vkGetPhysicalDeviceQueueFamilyProperties( + physicalDevice, + &queueFamilyCount, + queueProps + ); + + queueFamilyBest = 0; + *queueFamilyIndex = UINT32_MAX; + for (i = 0; i < queueFamilyCount; i += 1) + { + renderer->vkGetPhysicalDeviceSurfaceSupportKHR( + physicalDevice, + i, + surface, + &supportsPresent + ); + if ( !supportsPresent || + !(queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) ) + { + /* Not a graphics family, ignore. */ + continue; + } + + /* The queue family bitflags are kind of annoying. + * + * We of course need a graphics family, but we ideally want the + * _primary_ graphics family. The spec states that at least one + * graphics family must also be a compute family, so generally + * drivers make that the first one. But hey, maybe something + * genuinely can't do compute or something, and FNA doesn't + * need it, so we'll be open to a non-compute queue family. + * + * Additionally, it's common to see the primary queue family + * have the transfer bit set, which is great! But this is + * actually optional; it's impossible to NOT have transfers in + * graphics/compute but it _is_ possible for a graphics/compute + * family, even the primary one, to just decide not to set the + * bitflag. Admittedly, a driver may want to isolate transfer + * queues to a dedicated family so that queues made solely for + * transfers can have an optimized DMA queue. + * + * That, or the driver author got lazy and decided not to set + * the bit. Looking at you, Android. + * + * -flibit + */ + if (queueProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT) + { + if (queueProps[i].queueFlags & VK_QUEUE_TRANSFER_BIT) + { + /* Has all attribs! */ + queueFamilyRank = 3; + } + else + { + /* Probably has a DMA transfer queue family */ + queueFamilyRank = 2; + } + } + else + { + /* Just a graphics family, probably has something better */ + queueFamilyRank = 1; + } + if (queueFamilyRank > queueFamilyBest) + { + *queueFamilyIndex = i; + queueFamilyBest = queueFamilyRank; + } + } + + SDL_stack_free(queueProps); + + if (*queueFamilyIndex == UINT32_MAX) + { + /* Somehow no graphics queues existed. Compute-only device? */ + return 0; + } + + /* FIXME: Need better structure for checking vs storing support details */ + querySuccess = VULKAN_INTERNAL_QuerySwapchainSupport( + renderer, + physicalDevice, + surface, + &swapchainSupportDetails + ); + if (swapchainSupportDetails.formatsLength > 0) + { + SDL_free(swapchainSupportDetails.formats); + } + if (swapchainSupportDetails.presentModesLength > 0) + { + SDL_free(swapchainSupportDetails.presentModes); + } + + return ( querySuccess && + swapchainSupportDetails.formatsLength > 0 && + swapchainSupportDetails.presentModesLength > 0 ); +} + +static Uint8 VULKAN_INTERNAL_DeterminePhysicalDevice( + VulkanRenderer *renderer, + VkSurfaceKHR surface +) { + VkResult vulkanResult; + VkPhysicalDevice *physicalDevices; + VulkanExtensions *physicalDeviceExtensions; + Uint32 physicalDeviceCount, i, suitableIndex; + Uint32 queueFamilyIndex, suitableQueueFamilyIndex; + Uint8 deviceRank, highestRank; + + vulkanResult = renderer->vkEnumeratePhysicalDevices( + renderer->instance, + &physicalDeviceCount, + NULL + ); + VULKAN_ERROR_CHECK(vulkanResult, vkEnumeratePhysicalDevices, 0) + + if (physicalDeviceCount == 0) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to find any GPUs with Vulkan support"); + return 0; + } + + physicalDevices = SDL_stack_alloc(VkPhysicalDevice, physicalDeviceCount); + physicalDeviceExtensions = SDL_stack_alloc(VulkanExtensions, physicalDeviceCount); + + vulkanResult = renderer->vkEnumeratePhysicalDevices( + renderer->instance, + &physicalDeviceCount, + physicalDevices + ); + + /* This should be impossible to hit, but from what I can tell this can + * be triggered not because the array is too small, but because there + * were drivers that turned out to be bogus, so this is the loader's way + * of telling us that the list is now smaller than expected :shrug: + */ + if (vulkanResult == VK_INCOMPLETE) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "vkEnumeratePhysicalDevices returned VK_INCOMPLETE, will keep trying anyway..."); + vulkanResult = VK_SUCCESS; + } + + if (vulkanResult != VK_SUCCESS) + { + SDL_LogWarn( + SDL_LOG_CATEGORY_APPLICATION, + "vkEnumeratePhysicalDevices failed: %s", + VkErrorMessages(vulkanResult) + ); + SDL_stack_free(physicalDevices); + SDL_stack_free(physicalDeviceExtensions); + return 0; + } + + /* Any suitable device will do, but we'd like the best */ + suitableIndex = -1; + suitableQueueFamilyIndex = 0; + highestRank = 0; + for (i = 0; i < physicalDeviceCount; i += 1) + { + deviceRank = highestRank; + if (VULKAN_INTERNAL_IsDeviceSuitable( + renderer, + physicalDevices[i], + &physicalDeviceExtensions[i], + surface, + &queueFamilyIndex, + &deviceRank + )) { + /* Use this for rendering. + * Note that this may override a previous device that + * supports rendering, but shares the same device rank. + */ + suitableIndex = i; + suitableQueueFamilyIndex = queueFamilyIndex; + highestRank = deviceRank; + } + else if (deviceRank > highestRank) + { + /* In this case, we found a... "realer?" GPU, + * but it doesn't actually support our Vulkan. + * We should disqualify all devices below as a + * result, because if we don't we end up + * ignoring real hardware and risk using + * something like LLVMpipe instead! + * -flibit + */ + suitableIndex = -1; + highestRank = deviceRank; + } + } + + if (suitableIndex != -1) + { + renderer->supports = physicalDeviceExtensions[suitableIndex]; + renderer->physicalDevice = physicalDevices[suitableIndex]; + renderer->queueFamilyIndex = suitableQueueFamilyIndex; + } + else + { + SDL_stack_free(physicalDevices); + SDL_stack_free(physicalDeviceExtensions); + return 0; + } + + renderer->physicalDeviceProperties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + if (renderer->supports.KHR_driver_properties) + { + renderer->physicalDeviceDriverProperties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR; + renderer->physicalDeviceDriverProperties.pNext = NULL; + + renderer->physicalDeviceProperties.pNext = + &renderer->physicalDeviceDriverProperties; + } + else + { + renderer->physicalDeviceProperties.pNext = NULL; + } + + renderer->vkGetPhysicalDeviceProperties2KHR( + renderer->physicalDevice, + &renderer->physicalDeviceProperties + ); + + renderer->vkGetPhysicalDeviceMemoryProperties( + renderer->physicalDevice, + &renderer->memoryProperties + ); + + SDL_stack_free(physicalDevices); + SDL_stack_free(physicalDeviceExtensions); + return 1; +} + +static Uint8 VULKAN_INTERNAL_CreateLogicalDevice( + VulkanRenderer *renderer +) { + VkResult vulkanResult; + VkDeviceCreateInfo deviceCreateInfo; + VkPhysicalDeviceFeatures deviceFeatures; + VkPhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures; + const char **deviceExtensions; + + VkDeviceQueueCreateInfo queueCreateInfo; + float queuePriority = 1.0f; + + queueCreateInfo.sType = + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.pNext = NULL; + queueCreateInfo.flags = 0; + queueCreateInfo.queueFamilyIndex = renderer->queueFamilyIndex; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + + /* specifying used device features */ + + SDL_zero(deviceFeatures); + deviceFeatures.fillModeNonSolid = VK_TRUE; + deviceFeatures.samplerAnisotropy = VK_TRUE; + deviceFeatures.multiDrawIndirect = VK_TRUE; + deviceFeatures.independentBlend = VK_TRUE; + + /* creating the logical device */ + + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + if (renderer->supports.KHR_portability_subset) + { + portabilityFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR; + portabilityFeatures.pNext = NULL; + portabilityFeatures.constantAlphaColorBlendFactors = VK_FALSE; + portabilityFeatures.events = VK_FALSE; + portabilityFeatures.imageViewFormatReinterpretation = VK_FALSE; + portabilityFeatures.imageViewFormatSwizzle = VK_TRUE; + portabilityFeatures.imageView2DOn3DImage = VK_FALSE; + portabilityFeatures.multisampleArrayImage = VK_FALSE; + portabilityFeatures.mutableComparisonSamplers = VK_FALSE; + portabilityFeatures.pointPolygons = VK_FALSE; + portabilityFeatures.samplerMipLodBias = VK_FALSE; /* Technically should be true, but eh */ + portabilityFeatures.separateStencilMaskRef = VK_FALSE; + portabilityFeatures.shaderSampleRateInterpolationFunctions = VK_FALSE; + portabilityFeatures.tessellationIsolines = VK_FALSE; + portabilityFeatures.tessellationPointMode = VK_FALSE; + portabilityFeatures.triangleFans = VK_FALSE; + portabilityFeatures.vertexAttributeAccessBeyondStride = VK_FALSE; + deviceCreateInfo.pNext = &portabilityFeatures; + } + else + { + deviceCreateInfo.pNext = NULL; + } + deviceCreateInfo.flags = 0; + deviceCreateInfo.queueCreateInfoCount = 1; + deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; + deviceCreateInfo.enabledLayerCount = 0; + deviceCreateInfo.ppEnabledLayerNames = NULL; + deviceCreateInfo.enabledExtensionCount = GetDeviceExtensionCount( + &renderer->supports + ); + deviceExtensions = SDL_stack_alloc( + const char*, + deviceCreateInfo.enabledExtensionCount + ); + CreateDeviceExtensionArray(&renderer->supports, deviceExtensions); + deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions; + deviceCreateInfo.pEnabledFeatures = &deviceFeatures; + + vulkanResult = renderer->vkCreateDevice( + renderer->physicalDevice, + &deviceCreateInfo, + NULL, + &renderer->logicalDevice + ); + SDL_stack_free(deviceExtensions); + VULKAN_ERROR_CHECK(vulkanResult, vkCreateDevice, 0) + + /* Load vkDevice entry points */ + + #define VULKAN_DEVICE_FUNCTION(ext, ret, func, params) \ + renderer->func = (vkfntype_##func) \ + renderer->vkGetDeviceProcAddr( \ + renderer->logicalDevice, \ + #func \ + ); + #include "Refresh_vulkan_vkfuncs.h" + + renderer->vkGetDeviceQueue( + renderer->logicalDevice, + renderer->queueFamilyIndex, + 0, + &renderer->unifiedQueue + ); + + return 1; +} + +static void VULKAN_INTERNAL_LoadEntryPoints(void) +{ + /* Required for MoltenVK support */ + SDL_setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1); + + /* Load Vulkan entry points */ + if (SDL_Vulkan_LoadLibrary(NULL) < 0) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Vulkan: SDL_Vulkan_LoadLibrary failed!"); + return; + } + + #ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" + #endif + vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) SDL_Vulkan_GetVkGetInstanceProcAddr(); + #ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA + #pragma GCC diagnostic pop + #endif + if (vkGetInstanceProcAddr == NULL) + { + SDL_LogWarn( + SDL_LOG_CATEGORY_APPLICATION, + "SDL_Vulkan_GetVkGetInstanceProcAddr(): %s", + SDL_GetError() + ); + return; + } + + #define VULKAN_GLOBAL_FUNCTION(name) \ + name = (PFN_##name) vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \ + if (name == NULL) \ + { \ + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \ + return; \ + } + #include "Refresh_vulkan_vkfuncs.h" +} + +static Uint8 VULKAN_INTERNAL_PrepareVulkan( + VulkanRenderer *renderer +) { + SDL_Window *dummyWindowHandle; + VkSurfaceKHR surface; + SwapchainSupportDetails swapchainSupportDetails; + + VULKAN_INTERNAL_LoadEntryPoints(); + + dummyWindowHandle = SDL_CreateWindow( + "Refresh_ Vulkan", + 0, 0, + 128, 128, + SDL_WINDOW_VULKAN | SDL_WINDOW_HIDDEN + ); + + if (dummyWindowHandle == NULL) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Vulkan: Could not create dummy window"); + return 0; + } + + if (!VULKAN_INTERNAL_CreateInstance(renderer, dummyWindowHandle)) + { + SDL_DestroyWindow(dummyWindowHandle); + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Vulkan: Could not create Vulkan instance"); + return 0; + } + + if (!SDL_Vulkan_CreateSurface( + dummyWindowHandle, + renderer->instance, + &surface + )) { + SDL_DestroyWindow(dummyWindowHandle); + SDL_LogWarn( + SDL_LOG_CATEGORY_APPLICATION, + "SDL_Vulkan_CreateSurface failed: %s", + SDL_GetError() + ); + return 0; + } + + #define VULKAN_INSTANCE_FUNCTION(ext, ret, func, params) \ + renderer->func = (vkfntype_##func) vkGetInstanceProcAddr(renderer->instance, #func); + #include "Refresh_vulkan_vkfuncs.h" + + if (!VULKAN_INTERNAL_DeterminePhysicalDevice(renderer, surface)) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Vulkan: Failed to determine a suitable physical device"); + + renderer->vkDestroySurfaceKHR( + renderer->instance, + surface, + NULL + ); + + SDL_DestroyWindow(dummyWindowHandle); + return 0; + } + + if (!VULKAN_INTERNAL_QuerySwapchainSupport( + renderer, + renderer->physicalDevice, + surface, + &swapchainSupportDetails + )) + { + renderer->vkDestroySurfaceKHR( + renderer->instance, + surface, + NULL + ); + + SDL_DestroyWindow(dummyWindowHandle); + return 0; + } + + SDL_free(swapchainSupportDetails.formats); + SDL_free(swapchainSupportDetails.presentModes); + + renderer->vkDestroySurfaceKHR( + renderer->instance, + surface, + NULL + ); + + SDL_DestroyWindow(dummyWindowHandle); + return 1; +} + +static SDL_bool VULKAN_PrepareDriver() +{ + /* Set up dummy VulkanRenderer */ + VulkanRenderer *renderer; + SDL_bool result; + + if (SDL_Vulkan_LoadLibrary(NULL) < 0) + { + return 0; + } + + renderer = (VulkanRenderer*) SDL_malloc(sizeof(VulkanRenderer)); + SDL_memset(renderer, '\0', sizeof(VulkanRenderer)); + + result = VULKAN_INTERNAL_PrepareVulkan(renderer); + + if (result) + { + renderer->vkDestroyInstance(renderer->instance, NULL); + } + SDL_free(renderer); + SDL_Vulkan_UnloadLibrary(); + return result; +} + +static Refresh_Device* VULKAN_CreateDevice(SDL_bool debugMode) +{ + VulkanRenderer *renderer; + + Refresh_Device *result; + VkResult vulkanResult; + Uint32 i; + + /* Variables: Image Format Detection */ + VkImageFormatProperties imageFormatProperties; + + /* Variables: Query Pool Creation */ + VkQueryPoolCreateInfo queryPoolCreateInfo; + + /* Variables: Device Feature Checks */ + VkPhysicalDeviceFeatures physicalDeviceFeatures; + + if (SDL_Vulkan_LoadLibrary(NULL) < 0) + { + SDL_assert(!"This should have failed in PrepareDevice first!"); + return NULL; + } + + renderer = (VulkanRenderer*) SDL_malloc(sizeof(VulkanRenderer)); + SDL_memset(renderer, '\0', sizeof(VulkanRenderer)); + renderer->debugMode = debugMode; + + if (!VULKAN_INTERNAL_PrepareVulkan(renderer)) + { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize Vulkan!"); + SDL_free(renderer); + SDL_Vulkan_UnloadLibrary(); + return NULL; + } + + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Refresh_ Driver: Vulkan"); + SDL_LogInfo( + SDL_LOG_CATEGORY_APPLICATION, + "Vulkan Device: %s", + renderer->physicalDeviceProperties.properties.deviceName + ); + SDL_LogInfo( + SDL_LOG_CATEGORY_APPLICATION, + "Vulkan Driver: %s %s", + renderer->physicalDeviceDriverProperties.driverName, + renderer->physicalDeviceDriverProperties.driverInfo + ); + SDL_LogInfo( + SDL_LOG_CATEGORY_APPLICATION, + "Vulkan Conformance: %u.%u.%u", + renderer->physicalDeviceDriverProperties.conformanceVersion.major, + renderer->physicalDeviceDriverProperties.conformanceVersion.minor, + renderer->physicalDeviceDriverProperties.conformanceVersion.patch + ); + + if (!VULKAN_INTERNAL_CreateLogicalDevice( + renderer + )) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create logical device"); + SDL_free(renderer); + SDL_Vulkan_UnloadLibrary(); + return NULL; + } + + /* FIXME: just move this into this function */ + result = (Refresh_Device*) SDL_malloc(sizeof(Refresh_Device)); + ASSIGN_DRIVER(VULKAN) + + result->driverData = (Refresh_Renderer*) renderer; + + /* + * Create initial swapchain array + */ + + renderer->claimedWindowCapacity = 1; + renderer->claimedWindowCount = 0; + renderer->claimedWindows = SDL_malloc( + renderer->claimedWindowCapacity * sizeof(WindowData*) + ); + + /* Threading */ + + renderer->allocatorLock = SDL_CreateMutex(); + renderer->disposeLock = SDL_CreateMutex(); + renderer->submitLock = SDL_CreateMutex(); + renderer->acquireCommandBufferLock = SDL_CreateMutex(); + renderer->renderPassFetchLock = SDL_CreateMutex(); + renderer->framebufferFetchLock = SDL_CreateMutex(); + renderer->queryLock = SDL_CreateMutex(); + + /* + * Create submitted command buffer list + */ + + renderer->submittedCommandBufferCapacity = 16; + renderer->submittedCommandBufferCount = 0; + renderer->submittedCommandBuffers = SDL_malloc(sizeof(VulkanCommandBuffer*) * renderer->submittedCommandBufferCapacity); + + /* Memory Allocator */ + + renderer->memoryAllocator = (VulkanMemoryAllocator*) SDL_malloc( + sizeof(VulkanMemoryAllocator) + ); + + for (i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) + { + renderer->memoryAllocator->subAllocators[i].memoryTypeIndex = i; + renderer->memoryAllocator->subAllocators[i].allocations = NULL; + renderer->memoryAllocator->subAllocators[i].allocationCount = 0; + renderer->memoryAllocator->subAllocators[i].sortedFreeRegions = SDL_malloc( + sizeof(VulkanMemoryFreeRegion*) * 4 + ); + renderer->memoryAllocator->subAllocators[i].sortedFreeRegionCount = 0; + renderer->memoryAllocator->subAllocators[i].sortedFreeRegionCapacity = 4; + } + + /* UBO alignment */ + + renderer->minUBOAlignment = (Uint32) renderer->physicalDeviceProperties.properties.limits.minUniformBufferOffsetAlignment; + + /* Initialize query pool */ + + queryPoolCreateInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; + queryPoolCreateInfo.pNext = NULL; + queryPoolCreateInfo.flags = 0; + queryPoolCreateInfo.queryType = VK_QUERY_TYPE_OCCLUSION; + queryPoolCreateInfo.queryCount = MAX_QUERIES; + queryPoolCreateInfo.pipelineStatistics = 0; + + vulkanResult = renderer->vkCreateQueryPool( + renderer->logicalDevice, + &queryPoolCreateInfo, + NULL, + &renderer->queryPool + ); + VULKAN_ERROR_CHECK(vulkanResult, vkCreateQueryPool, NULL) + + for (i = 0; i < MAX_QUERIES - 1; i += 1) + { + renderer->freeQueryIndexStack[i] = i + 1; + } + renderer->freeQueryIndexStack[MAX_QUERIES - 1] = -1; + + /* Initialize caches */ + + for (i = 0; i < NUM_COMMAND_POOL_BUCKETS; i += 1) + { + renderer->commandPoolHashTable.buckets[i].elements = NULL; + renderer->commandPoolHashTable.buckets[i].count = 0; + renderer->commandPoolHashTable.buckets[i].capacity = 0; + } + + renderer->renderPassHashArray.elements = NULL; + renderer->renderPassHashArray.count = 0; + renderer->renderPassHashArray.capacity = 0; + + renderer->framebufferHashArray.elements = NULL; + renderer->framebufferHashArray.count = 0; + renderer->framebufferHashArray.capacity = 0; + + /* Initialize fence pool */ + + renderer->fencePool.lock = SDL_CreateMutex(); + + renderer->fencePool.availableFenceCapacity = 4; + renderer->fencePool.availableFenceCount = 0; + renderer->fencePool.availableFences = SDL_malloc( + renderer->fencePool.availableFenceCapacity * sizeof(VulkanFenceHandle*) + ); + + /* Some drivers don't support D16, so we have to fall back to D32. */ + + vulkanResult = renderer->vkGetPhysicalDeviceImageFormatProperties( + renderer->physicalDevice, + VK_FORMAT_D16_UNORM, + VK_IMAGE_TYPE_2D, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_ASPECT_DEPTH_BIT, + 0, + &imageFormatProperties + ); + + if (vulkanResult == VK_ERROR_FORMAT_NOT_SUPPORTED) + { + renderer->D16Format = VK_FORMAT_D32_SFLOAT; + } + else + { + renderer->D16Format = VK_FORMAT_D16_UNORM; + } + + vulkanResult = renderer->vkGetPhysicalDeviceImageFormatProperties( + renderer->physicalDevice, + VK_FORMAT_D16_UNORM_S8_UINT, + VK_IMAGE_TYPE_2D, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, + 0, + &imageFormatProperties + ); + + if (vulkanResult == VK_ERROR_FORMAT_NOT_SUPPORTED) + { + renderer->D16S8Format = VK_FORMAT_D32_SFLOAT_S8_UINT; + } + else + { + renderer->D16S8Format = VK_FORMAT_D16_UNORM_S8_UINT; + } + + /* Deferred destroy storage */ + + renderer->texturesToDestroyCapacity = 16; + renderer->texturesToDestroyCount = 0; + + renderer->texturesToDestroy = (VulkanTexture**)SDL_malloc( + sizeof(VulkanTexture*) * + renderer->texturesToDestroyCapacity + ); + + renderer->buffersToDestroyCapacity = 16; + renderer->buffersToDestroyCount = 0; + + renderer->buffersToDestroy = SDL_malloc( + sizeof(VulkanBuffer*) * + renderer->buffersToDestroyCapacity + ); + + renderer->samplersToDestroyCapacity = 16; + renderer->samplersToDestroyCount = 0; + + renderer->samplersToDestroy = SDL_malloc( + sizeof(VulkanSampler*) * + renderer->samplersToDestroyCapacity + ); + + renderer->graphicsPipelinesToDestroyCapacity = 16; + renderer->graphicsPipelinesToDestroyCount = 0; + + renderer->graphicsPipelinesToDestroy = SDL_malloc( + sizeof(VulkanGraphicsPipeline*) * + renderer->graphicsPipelinesToDestroyCapacity + ); + + renderer->computePipelinesToDestroyCapacity = 16; + renderer->computePipelinesToDestroyCount = 0; + + renderer->computePipelinesToDestroy = SDL_malloc( + sizeof(VulkanComputePipeline*) * + renderer->computePipelinesToDestroyCapacity + ); + + renderer->shadersToDestroyCapacity = 16; + renderer->shadersToDestroyCount = 0; + + renderer->shadersToDestroy = SDL_malloc( + sizeof(VulkanShader*) * + renderer->shadersToDestroyCapacity + ); + + renderer->framebuffersToDestroyCapacity = 16; + renderer->framebuffersToDestroyCount = 0; + renderer->framebuffersToDestroy = SDL_malloc( + sizeof(VulkanFramebuffer*) * + renderer->framebuffersToDestroyCapacity + ); + + /* Defrag state */ + + renderer->defragInProgress = 0; + + renderer->allocationsToDefragCount = 0; + renderer->allocationsToDefragCapacity = 4; + renderer->allocationsToDefrag = SDL_malloc( + renderer->allocationsToDefragCapacity * sizeof(VulkanMemoryAllocation*) + ); + + /* Support checks */ + + renderer->vkGetPhysicalDeviceFeatures( + renderer->physicalDevice, + &physicalDeviceFeatures + ); + + renderer->supportsPreciseOcclusionQueries = physicalDeviceFeatures.occlusionQueryPrecise; + + return result; +} + +Refresh_Driver VulkanDriver = { + "Vulkan", + REFRESH_BACKEND_VULKAN, + VULKAN_PrepareDriver, + VULKAN_CreateDevice +}; + +#endif /* REFRESH_VULKAN */ diff --git a/src/Refresh_Driver_Vulkan_vkfuncs.h b/src/vulkan/Refresh_vulkan_vkfuncs.h similarity index 77% rename from src/Refresh_Driver_Vulkan_vkfuncs.h rename to src/vulkan/Refresh_vulkan_vkfuncs.h index 47749ff..da2001c 100644 --- a/src/Refresh_Driver_Vulkan_vkfuncs.h +++ b/src/vulkan/Refresh_vulkan_vkfuncs.h @@ -1,6 +1,6 @@ -/* Refresh - XNA-inspired 3D Graphics Library with modern capabilities +/* Refresh - a cross-platform hardware-accelerated graphics library with modern capabilities * - * Copyright (c) 2020 Evan Hemsley + * Copyright (c) 2020-2024 Evan Hemsley * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from @@ -46,22 +46,23 @@ VULKAN_INSTANCE_FUNCTION(BaseVK, PFN_vkVoidFunction, vkGetDeviceProcAddr, (VkDev VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkCreateDevice, (VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice)) VULKAN_INSTANCE_FUNCTION(BaseVK, void, vkDestroyInstance, (VkInstance instance, const VkAllocationCallbacks *pAllocator)) VULKAN_INSTANCE_FUNCTION(BaseVK, void, vkDestroySurfaceKHR, (VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks *pAllocator)) -VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkEnumerateDeviceExtensionProperties, (VkPhysicalDevice physicalDevice, const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties)) -VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkEnumeratePhysicalDevices, (VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices)) +VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkEnumerateDeviceExtensionProperties, (VkPhysicalDevice physicalDevice, const char *pLayerName, Uint32 *pPropertyCount, VkExtensionProperties *pProperties)) +VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkEnumeratePhysicalDevices, (VkInstance instance, Uint32 *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices)) VULKAN_INSTANCE_FUNCTION(BaseVK, void, vkGetPhysicalDeviceFeatures, (VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures *pFeatures)) VULKAN_INSTANCE_FUNCTION(BaseVK, void, vkGetPhysicalDeviceFormatProperties, (VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties *pFormatProperties)) VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkGetPhysicalDeviceImageFormatProperties, (VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties *pImageFormatProperties)) VULKAN_INSTANCE_FUNCTION(BaseVK, void, vkGetPhysicalDeviceMemoryProperties, (VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties *pMemoryProperties)) VULKAN_INSTANCE_FUNCTION(BaseVK, void, vkGetPhysicalDeviceProperties, (VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties *pProperties)) VULKAN_INSTANCE_FUNCTION(BaseVK, void, vkGetPhysicalDeviceProperties2KHR, (VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2 *pProperties)) -VULKAN_INSTANCE_FUNCTION(BaseVK, void, vkGetPhysicalDeviceQueueFamilyProperties, (VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties *pQueueFamilyProperties)) +VULKAN_INSTANCE_FUNCTION(BaseVK, void, vkGetPhysicalDeviceQueueFamilyProperties, (VkPhysicalDevice physicalDevice, Uint32 *pQueueFamilyPropertyCount, VkQueueFamilyProperties *pQueueFamilyProperties)) VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkGetPhysicalDeviceSurfaceCapabilitiesKHR, (VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR *pSurfaceCapabilities)) -VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkGetPhysicalDeviceSurfaceFormatsKHR, (VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pSurfaceFormatCount, VkSurfaceFormatKHR *pSurfaceFormats)) -VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkGetPhysicalDeviceSurfacePresentModesKHR, (VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pPresentModeCount, VkPresentModeKHR *pPresentModes)) -VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkGetPhysicalDeviceSurfaceSupportKHR, (VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32 *pSupported)) +VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkGetPhysicalDeviceSurfaceFormatsKHR, (VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, Uint32 *pSurfaceFormatCount, VkSurfaceFormatKHR *pSurfaceFormats)) +VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkGetPhysicalDeviceSurfacePresentModesKHR, (VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, Uint32 *pPresentModeCount, VkPresentModeKHR *pPresentModes)) +VULKAN_INSTANCE_FUNCTION(BaseVK, VkResult, vkGetPhysicalDeviceSurfaceSupportKHR, (VkPhysicalDevice physicalDevice, Uint32 queueFamilyIndex, VkSurfaceKHR surface, VkBool32 *pSupported)) /* Optional debug feature, used by SetStringMarker */ VULKAN_INSTANCE_FUNCTION(VK_EXT_debug_utils, void, vkCmdInsertDebugUtilsLabelEXT, (VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT *pMarkerInfo)) +VULKAN_INSTANCE_FUNCTION(VK_EXT_debug_utils, void, vkSetDebugUtilsObjectNameEXT, (VkDevice device, const VkDebugUtilsObjectNameInfoEXT* pNameInfo)) /* * vkDevice, created by a vkInstance @@ -70,7 +71,7 @@ VULKAN_INSTANCE_FUNCTION(VK_EXT_debug_utils, void, vkCmdInsertDebugUtilsLabelEXT #ifndef VULKAN_DEVICE_FUNCTION #define VULKAN_DEVICE_FUNCTION(ext, ret, func, params) #endif -VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkAcquireNextImageKHR, (VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *pImageIndex)) +VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkAcquireNextImageKHR, (VkDevice device, VkSwapchainKHR swapchain, Uint64 timeout, VkSemaphore semaphore, VkFence fence, Uint32 *pImageIndex)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkAllocateCommandBuffers, (VkDevice device, const VkCommandBufferAllocateInfo *pAllocateInfo, VkCommandBuffer *pCommandBuffers)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkAllocateDescriptorSets, (VkDevice device, const VkDescriptorSetAllocateInfo *pAllocateInfo, VkDescriptorSet *pDescriptorSets)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkAllocateMemory, (VkDevice device, const VkMemoryAllocateInfo *pAllocateInfo, const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMemory)) @@ -78,38 +79,39 @@ VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkBeginCommandBuffer, (VkCommandBuffer VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkBindBufferMemory, (VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkBindImageMemory, (VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset)) VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdBeginRenderPass, (VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, VkSubpassContents contents)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdBindDescriptorSets, (VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t *pDynamicOffsets)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdBindDescriptorSets, (VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, Uint32 firstSet, Uint32 descriptorSetCount, const VkDescriptorSet *pDescriptorSets, Uint32 dynamicOffsetCount, const Uint32 *pDynamicOffsets)) VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdBindIndexBuffer, (VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType)) VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdBindPipeline, (VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdBindVertexBuffers, (VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer *pBuffers, const VkDeviceSize *pOffsets)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdBlitImage, (VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit *pRegions, VkFilter filter)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdClearAttachments, (VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkClearAttachment *pAttachments, uint32_t rectCount, const VkClearRect *pRects)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdClearColorImage, (VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue *pColor, uint32_t rangeCount, const VkImageSubresourceRange *pRanges)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdClearDepthStencilImage, (VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue *pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange *pRanges)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdCopyBuffer, (VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdCopyBufferToImage, (VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy *pRegions)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdCopyImageToBuffer, (VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy *pRegions)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdCopyImage, (VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdDispatch, (VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdDraw, (VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdDrawIndexed, (VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdDrawIndirect, (VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdBindVertexBuffers, (VkCommandBuffer commandBuffer, Uint32 firstBinding, Uint32 bindingCount, const VkBuffer *pBuffers, const VkDeviceSize *pOffsets)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdBlitImage, (VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, Uint32 regionCount, const VkImageBlit *pRegions, VkFilter filter)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdClearAttachments, (VkCommandBuffer commandBuffer, Uint32 attachmentCount, const VkClearAttachment *pAttachments, Uint32 rectCount, const VkClearRect *pRects)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdClearColorImage, (VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue *pColor, Uint32 rangeCount, const VkImageSubresourceRange *pRanges)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdClearDepthStencilImage, (VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue *pDepthStencil, Uint32 rangeCount, const VkImageSubresourceRange *pRanges)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdCopyBuffer, (VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, Uint32 regionCount, const VkBufferCopy* pRegions)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdCopyBufferToImage, (VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, Uint32 regionCount, const VkBufferImageCopy *pRegions)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdCopyImageToBuffer, (VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, Uint32 regionCount, const VkBufferImageCopy *pRegions)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdCopyImage, (VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, Uint32 regionCount, const VkImageCopy* pRegions)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdDispatch, (VkCommandBuffer commandBuffer, Uint32 groupCountX, Uint32 groupCountY, Uint32 groupCountZ)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdDraw, (VkCommandBuffer commandBuffer, Uint32 vertexCount, Uint32 instanceCount, Uint32 firstVertex, Uint32 firstInstance)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdDrawIndexed, (VkCommandBuffer commandBuffer, Uint32 indexCount, Uint32 instanceCount, Uint32 firstIndex, Sint32 vertexOffset, Uint32 firstInstance)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdDrawIndirect, (VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, Uint32 drawCount, Uint32 stride)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdDrawIndexedIndirect, (VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, Uint32 drawCount, Uint32 stride)) VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdEndRenderPass, (VkCommandBuffer commandBuffer)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdPipelineBarrier, (VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdResolveImage, (VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve *pRegions)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdPipelineBarrier, (VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, Uint32 memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, Uint32 bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers, Uint32 imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdResolveImage, (VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, Uint32 regionCount, const VkImageResolve *pRegions)) VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdSetBlendConstants, (VkCommandBuffer commandBuffer, const float blendConstants[4])) VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdSetDepthBias, (VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdSetScissor, (VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D *pScissors)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdSetStencilReference, (VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t reference)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdSetViewport, (VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport *pViewports)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdSetScissor, (VkCommandBuffer commandBuffer, Uint32 firstScissor, Uint32 scissorCount, const VkRect2D *pScissors)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdSetStencilReference, (VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, Uint32 reference)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdSetViewport, (VkCommandBuffer commandBuffer, Uint32 firstViewport, Uint32 viewportCount, const VkViewport *pViewports)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateBuffer, (VkDevice device, const VkBufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkBuffer *pBuffer)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateCommandPool, (VkDevice device, const VkCommandPoolCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkCommandPool *pCommandPool)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateDescriptorPool, (VkDevice device, const VkDescriptorPoolCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDescriptorPool *pDescriptorPool)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateDescriptorSetLayout, (VkDevice device, const VkDescriptorSetLayoutCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDescriptorSetLayout *pSetLayout)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateFence, (VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateFramebuffer, (VkDevice device, const VkFramebufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFramebuffer *pFramebuffer)) -VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateComputePipelines, (VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo *pCreateInfos, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines)) -VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateGraphicsPipelines, (VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo *pCreateInfos, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines)) +VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateComputePipelines, (VkDevice device, VkPipelineCache pipelineCache, Uint32 createInfoCount, const VkComputePipelineCreateInfo *pCreateInfos, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines)) +VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateGraphicsPipelines, (VkDevice device, VkPipelineCache pipelineCache, Uint32 createInfoCount, const VkGraphicsPipelineCreateInfo *pCreateInfos, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateImage, (VkDevice device, const VkImageCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImage *pImage)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreateImageView, (VkDevice device, const VkImageViewCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImageView *pView)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkCreatePipelineCache, (VkDevice device, const VkPipelineCacheCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkPipelineCache *pPipelineCache)) @@ -140,29 +142,29 @@ VULKAN_DEVICE_FUNCTION(BaseVK, void, vkDestroySwapchainKHR, (VkDevice device, Vk VULKAN_DEVICE_FUNCTION(BaseVK, void, vkDestroyQueryPool, (VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks *pAllocator)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkDeviceWaitIdle, (VkDevice device)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkEndCommandBuffer, (VkCommandBuffer commandBuffer)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkFreeCommandBuffers, (VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers)) -VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkFreeDescriptorSets, (VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkFreeCommandBuffers, (VkDevice device, VkCommandPool commandPool, Uint32 commandBufferCount, const VkCommandBuffer *pCommandBuffers)) +VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkFreeDescriptorSets, (VkDevice device, VkDescriptorPool descriptorPool, Uint32 descriptorSetCount, const VkDescriptorSet *pDescriptorSets)) VULKAN_DEVICE_FUNCTION(BaseVK, void, vkFreeMemory, (VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks *pAllocator)) VULKAN_DEVICE_FUNCTION(BaseVK, void, vkGetBufferMemoryRequirements2KHR, (VkDevice device, const VkBufferMemoryRequirementsInfo2 *pInfo, VkMemoryRequirements2 *pMemoryRequirements)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkGetDeviceQueue, (VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue *pQueue)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkGetDeviceQueue, (VkDevice device, Uint32 queueFamilyIndex, Uint32 queueIndex, VkQueue *pQueue)) VULKAN_DEVICE_FUNCTION(BaseVK, void, vkGetImageMemoryRequirements2KHR, (VkDevice device, const VkImageMemoryRequirementsInfo2 *pInfo, VkMemoryRequirements2 *pMemoryRequirements)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkGetFenceStatus, (VkDevice device, VkFence fence)) -VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkGetSwapchainImagesKHR, (VkDevice device, VkSwapchainKHR swapchain, uint32_t *pSwapchainImageCount, VkImage *pSwapchainImages)) +VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkGetSwapchainImagesKHR, (VkDevice device, VkSwapchainKHR swapchain, Uint32 *pSwapchainImageCount, VkImage *pSwapchainImages)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkMapMemory, (VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void **ppData)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkQueuePresentKHR, (VkQueue queue, const VkPresentInfoKHR *pPresentInfo)) -VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkQueueSubmit, (VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence)) +VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkQueueSubmit, (VkQueue queue, Uint32 submitCount, const VkSubmitInfo *pSubmits, VkFence fence)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkQueueWaitIdle, (VkQueue queue)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkResetCommandBuffer, (VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkResetCommandPool, (VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags)) VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkResetDescriptorPool, (VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags)) -VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkResetFences, (VkDevice device, uint32_t fenceCount, const VkFence *pFences)) +VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkResetFences, (VkDevice device, Uint32 fenceCount, const VkFence *pFences)) VULKAN_DEVICE_FUNCTION(BaseVK, void, vkUnmapMemory, (VkDevice device, VkDeviceMemory memory)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkUpdateDescriptorSets, (VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet *pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet *pDescriptorCopies)) -VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkWaitForFences, (VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll, uint64_t timeout)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdResetQueryPool, (VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdBeginQuery, (VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags)) -VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdEndQuery, (VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query)) -VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkGetQueryPoolResults, (VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void *pData, VkDeviceSize stride, VkQueryResultFlags flags)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkUpdateDescriptorSets, (VkDevice device, Uint32 descriptorWriteCount, const VkWriteDescriptorSet *pDescriptorWrites, Uint32 descriptorCopyCount, const VkCopyDescriptorSet *pDescriptorCopies)) +VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkWaitForFences, (VkDevice device, Uint32 fenceCount, const VkFence *pFences, VkBool32 waitAll, Uint64 timeout)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdResetQueryPool, (VkCommandBuffer commandBuffer, VkQueryPool queryPool, Uint32 firstQuery, Uint32 queryCount)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdBeginQuery, (VkCommandBuffer commandBuffer, VkQueryPool queryPool, Uint32 query, VkQueryControlFlags flags)) +VULKAN_DEVICE_FUNCTION(BaseVK, void, vkCmdEndQuery, (VkCommandBuffer commandBuffer, VkQueryPool queryPool, Uint32 query)) +VULKAN_DEVICE_FUNCTION(BaseVK, VkResult, vkGetQueryPoolResults, (VkDevice device, VkQueryPool queryPool, Uint32 firstQuery, Uint32 queryCount, size_t dataSize, void *pData, VkDeviceSize stride, VkQueryResultFlags flags)) /* * Redefine these every time you include this header!