├── shaders ├── rtprimitives.glsl-fs ├── vertex_color.glsl-fs ├── shader.glsl-fs ├── fullscreentri.glsl-vs ├── vertex_color.glsl-vs ├── shader.glsl-vs ├── build.ninja ├── shaders.vcxproj.filters └── shaders.vcxproj ├── .gitignore ├── LICENSE ├── Vulkan.vcxproj.filters ├── Vulkan.sln ├── bits.h ├── Vulkan.vcxproj └── main.c /shaders/rtprimitives.glsl-fs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sopyer/Vulkan/HEAD/shaders/rtprimitives.glsl-fs -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.user 3 | *.userosscache 4 | *.sln.docstates 5 | 6 | build/ 7 | 8 | .vs/ 9 | ipch/ 10 | *.ncb 11 | *.opendb 12 | *.opensdf 13 | *.sdf 14 | *.VC.db 15 | *.vspx 16 | *.tmp 17 | *.spv* 18 | 19 | -------------------------------------------------------------------------------- /shaders/vertex_color.glsl-fs: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec4 vColor; 5 | 6 | layout(location = 0) out vec4 rt0; 7 | 8 | void main() 9 | { 10 | rt0 = vColor; 11 | } -------------------------------------------------------------------------------- /shaders/shader.glsl-fs: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 fragColor; 5 | 6 | layout(location = 0) out vec4 outColor; 7 | 8 | void main() 9 | { 10 | outColor = vec4(fragColor, 1.0); 11 | } -------------------------------------------------------------------------------- /shaders/fullscreentri.glsl-vs: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | out gl_PerVertex 5 | { 6 | vec4 gl_Position; 7 | }; 8 | 9 | vec2 positions[3] = vec2[]( 10 | vec2(-1.0, 1.0), 11 | vec2(-1.0, -3.0), 12 | vec2( 3.0, 1.0) 13 | ); 14 | 15 | void main() 16 | { 17 | gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 18 | } 19 | -------------------------------------------------------------------------------- /shaders/vertex_color.glsl-vs: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec2 aPos; 5 | layout(location = 1) in vec4 aColor; 6 | 7 | out gl_PerVertex 8 | { 9 | vec4 gl_Position; 10 | }; 11 | layout(location = 0) out vec4 vColor; 12 | 13 | void main() 14 | { 15 | gl_Position = vec4(aPos, 0.0, 1.0); 16 | vColor = aColor; 17 | } 18 | -------------------------------------------------------------------------------- /shaders/shader.glsl-vs: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | out gl_PerVertex 5 | { 6 | vec4 gl_Position; 7 | }; 8 | 9 | layout(location = 0) out vec3 fragColor; 10 | 11 | vec2 positions[3] = vec2[]( 12 | vec2(0.0, -0.5), 13 | vec2(0.5, 0.5), 14 | vec2(-0.5, 0.5) 15 | ); 16 | 17 | vec3 colors[3] = vec3[]( 18 | vec3(1.0, 0.0, 0.0), 19 | vec3(0.0, 1.0, 0.0), 20 | vec3(0.0, 0.0, 1.0) 21 | ); 22 | 23 | void main() 24 | { 25 | gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 26 | fragColor = colors[gl_VertexIndex]; 27 | } 28 | -------------------------------------------------------------------------------- /shaders/build.ninja: -------------------------------------------------------------------------------- 1 | builddir = ../build/temp/shaders 2 | 3 | compile_glsl_vertex = cmd /c %VULKAN_SDK%\Bin32\glslangValidator -S vert 4 | compile_glsl_fragment = cmd /c %VULKAN_SDK%\Bin32\glslangValidator -S frag 5 | 6 | rule compile_glsl_vs 7 | command = $compile_glsl_vertex -V $in -o $out 8 | 9 | rule compile_glsl_fs 10 | command = $compile_glsl_fragment -V $in -o $out 11 | 12 | build vertex_color.spv-vs: compile_glsl_vs vertex_color.glsl-vs 13 | build vertex_color.spv-fs: compile_glsl_fs vertex_color.glsl-fs 14 | build shader.spv-vs: compile_glsl_vs shader.glsl-vs 15 | build fullscreentri.spv-vs: compile_glsl_vs fullscreentri.glsl-vs 16 | build shader.spv-fs: compile_glsl_fs shader.glsl-fs 17 | build rtprimitives.spv-fs: compile_glsl_fs rtprimitives.glsl-fs 18 | -------------------------------------------------------------------------------- /shaders/shaders.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {618911ff-df2a-45ae-bc09-2deaca382cfa} 6 | 7 | 8 | 9 | 10 | shaders 11 | 12 | 13 | shaders 14 | 15 | 16 | shaders 17 | 18 | 19 | shaders 20 | 21 | 22 | 23 | shaders 24 | 25 | 26 | shaders 27 | 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mykhailo Parfeniuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Vulkan.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | -------------------------------------------------------------------------------- /Vulkan.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Vulkan", "Vulkan.vcxproj", "{EE3884AC-7E9E-4109-A512-92C650FDC095}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {727CB19D-59E2-4B58-9A25-55921D415136} = {727CB19D-59E2-4B58-9A25-55921D415136} 9 | EndProjectSection 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders", "shaders\shaders.vcxproj", "{727CB19D-59E2-4B58-9A25-55921D415136}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|x64 = Debug|x64 16 | Debug|x86 = Debug|x86 17 | Release|x64 = Release|x64 18 | Release|x86 = Release|x86 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {EE3884AC-7E9E-4109-A512-92C650FDC095}.Debug|x64.ActiveCfg = Debug|x64 22 | {EE3884AC-7E9E-4109-A512-92C650FDC095}.Debug|x64.Build.0 = Debug|x64 23 | {EE3884AC-7E9E-4109-A512-92C650FDC095}.Debug|x86.ActiveCfg = Debug|Win32 24 | {EE3884AC-7E9E-4109-A512-92C650FDC095}.Debug|x86.Build.0 = Debug|Win32 25 | {EE3884AC-7E9E-4109-A512-92C650FDC095}.Release|x64.ActiveCfg = Release|x64 26 | {EE3884AC-7E9E-4109-A512-92C650FDC095}.Release|x64.Build.0 = Release|x64 27 | {EE3884AC-7E9E-4109-A512-92C650FDC095}.Release|x86.ActiveCfg = Release|Win32 28 | {EE3884AC-7E9E-4109-A512-92C650FDC095}.Release|x86.Build.0 = Release|Win32 29 | {727CB19D-59E2-4B58-9A25-55921D415136}.Debug|x64.ActiveCfg = Debug|x64 30 | {727CB19D-59E2-4B58-9A25-55921D415136}.Debug|x64.Build.0 = Debug|x64 31 | {727CB19D-59E2-4B58-9A25-55921D415136}.Debug|x86.ActiveCfg = Debug|Win32 32 | {727CB19D-59E2-4B58-9A25-55921D415136}.Debug|x86.Build.0 = Debug|Win32 33 | {727CB19D-59E2-4B58-9A25-55921D415136}.Release|x64.ActiveCfg = Release|x64 34 | {727CB19D-59E2-4B58-9A25-55921D415136}.Release|x64.Build.0 = Release|x64 35 | {727CB19D-59E2-4B58-9A25-55921D415136}.Release|x86.ActiveCfg = Release|Win32 36 | {727CB19D-59E2-4B58-9A25-55921D415136}.Release|x86.Build.0 = Release|Win32 37 | EndGlobalSection 38 | GlobalSection(SolutionProperties) = preSolution 39 | HideSolutionNode = FALSE 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /bits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /* 6 | ** Architecture-specific bit manipulation routines. 7 | ** 8 | ** Most modern processors provide instructions to count leading zeroes 9 | ** in a word, find the lowest and highest set bit, etc. These 10 | ** specific implementations will be used when available, falling back 11 | ** to a reasonably efficient generic implementation. 12 | ** 13 | ** bit_ffs/bit_fls returning value 0..31. 14 | ** ffs/fls return 1-32 by default, returning 0 for error. 15 | */ 16 | 17 | /* 18 | ** gcc 3.4 and above have builtin support, specialized for architecture. 19 | ** Some compilers masquerade as gcc; patchlevel test filters them out. 20 | */ 21 | #if defined (__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) \ 22 | && defined (__GNUC_PATCHLEVEL__) 23 | 24 | inline uint32_t bit_ffs32(uint32_t word) 25 | { 26 | return __builtin_ffs(word) - 1; 27 | } 28 | 29 | inline uint32_t bit_fls32(uint32_t word) 30 | { 31 | const uint32_t bit = word ? 32 - __builtin_clz(word) : 0; 32 | return bit - 1; 33 | } 34 | 35 | #elif defined (_MSC_VER) && (_MSC_VER >= 1400) && defined (_M_IX86) 36 | /* Microsoft Visual C++ support on x86/X64 architectures. */ 37 | 38 | #include 39 | 40 | #pragma intrinsic(_BitScanReverse) 41 | #pragma intrinsic(_BitScanForward) 42 | 43 | inline uint32_t bit_fls32(uint32_t word) 44 | { 45 | uint32_t index; 46 | return _BitScanReverse(&index, word) ? index : -1; 47 | } 48 | 49 | inline uint32_t bit_ffs32(uint32_t word) 50 | { 51 | uint32_t index; 52 | return _BitScanForward(&index, word) ? index : -1; 53 | } 54 | 55 | inline uint32_t bit_fls64(uint64_t word) 56 | { 57 | uint32_t index; 58 | if (_BitScanReverse(&index, word>>32)) return index+32; 59 | return _BitScanReverse(&index, word&0xFFFFFFFF) ? index : UINT32_MAX; 60 | } 61 | 62 | inline uint32_t bit_ffs64(uint64_t word) 63 | { 64 | uint32_t index; 65 | if (_BitScanForward(&index, word&0xFFFFFFFF)) return index; 66 | return _BitScanForward(&index, word>>32) ? index+32 : UINT32_MAX; 67 | } 68 | 69 | #elif defined (_MSC_VER) && (_MSC_VER >= 1400) && defined (_M_X64) 70 | /* Microsoft Visual C++ support on x86/X64 architectures. */ 71 | 72 | #include 73 | 74 | #pragma intrinsic(_BitScanReverse) 75 | #pragma intrinsic(_BitScanForward) 76 | 77 | inline uint32_t bit_fls32(uint32_t word) 78 | { 79 | uint32_t index; 80 | return _BitScanReverse(&index, word) ? index : -1; 81 | } 82 | 83 | inline uint32_t bit_ffs32(uint32_t word) 84 | { 85 | uint32_t index; 86 | return _BitScanForward(&index, word) ? index : -1; 87 | } 88 | 89 | inline uint32_t bit_fls64(uint64_t word) 90 | { 91 | uint32_t index; 92 | return _BitScanReverse64(&index, word) ? index : UINT32_MAX; 93 | } 94 | 95 | inline uint32_t bit_ffs64(uint64_t word) 96 | { 97 | uint32_t index; 98 | return _BitScanForward64(&index, word) ? index : UINT32_MAX; 99 | } 100 | 101 | #elif defined (__ARMCC_VERSION) 102 | /* RealView Compilation Tools for ARM */ 103 | 104 | inline uint32_t bit_ffs32(uint32_t word) 105 | { 106 | const uint32_t reverse = word & (~word + 1); 107 | const uint32_t bit = 32 - __clz(reverse); 108 | return bit - 1; 109 | } 110 | 111 | inline uint32_t bit_fls32(uint32_t word) 112 | { 113 | const uint32_t bit = word ? 32 - __clz(word) : 0; 114 | return bit - 1; 115 | } 116 | 117 | #else 118 | /* Fall back to generic implementation. */ 119 | 120 | inline uint32_t bit_fls_generic(uint32_t word) 121 | { 122 | uint32_t bit = 32; 123 | 124 | if (!word) bit -= 1; 125 | if (!(word & 0xffff0000)) { word <<= 16; bit -= 16; } 126 | if (!(word & 0xff000000)) { word <<= 8; bit -= 8; } 127 | if (!(word & 0xf0000000)) { word <<= 4; bit -= 4; } 128 | if (!(word & 0xc0000000)) { word <<= 2; bit -= 2; } 129 | if (!(word & 0x80000000)) { word <<= 1; bit -= 1; } 130 | 131 | return bit; 132 | } 133 | 134 | /* Implement ffs in terms of fls. */ 135 | inline uint32_t bit_ffs32(uint32_t word) 136 | { 137 | return bit_fls_generic(word & (~word + 1)) - 1; 138 | } 139 | 140 | inline uint32_t bit_fls32(uint32_t word) 141 | { 142 | return bit_fls_generic(word) - 1; 143 | } 144 | 145 | #endif 146 | 147 | inline uint32_t bit_is_pow2(uint32_t x) 148 | { 149 | return (x != 0) && ((x & (x - 1)) == 0); 150 | } 151 | 152 | inline uint32_t bit_align_up(uint32_t size, uint32_t align) 153 | { 154 | assert(bit_is_pow2(align)); 155 | return (size + (align - 1)) & ~(align - 1); 156 | } 157 | -------------------------------------------------------------------------------- /shaders/shaders.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {727CB19D-59E2-4B58-9A25-55921D415136} 23 | shaders 24 | 8.1 25 | 26 | 27 | 28 | Makefile 29 | true 30 | v140 31 | MultiByte 32 | 33 | 34 | Makefile 35 | false 36 | v140 37 | true 38 | MultiByte 39 | 40 | 41 | Makefile 42 | true 43 | v140 44 | MultiByte 45 | 46 | 47 | Makefile 48 | false 49 | v140 50 | true 51 | MultiByte 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | $(SolutionDir)build\bin_$(Configuration)_$(Platform)\ 73 | $(SolutionDir)build\temp\$(Configuration)_$(Platform)\ 74 | ninja 75 | 76 | 77 | $(SolutionDir)build\bin_$(Configuration)_$(Platform)\ 78 | $(SolutionDir)build\temp\$(Configuration)_$(Platform)\ 79 | ninja 80 | 81 | 82 | $(SolutionDir)build\bin_$(Configuration)_$(Platform)\ 83 | $(SolutionDir)build\temp\$(Configuration)_$(Platform)\ 84 | ninja 85 | 86 | 87 | $(SolutionDir)build\bin_$(Configuration)_$(Platform)\ 88 | $(SolutionDir)build\temp\$(Configuration)_$(Platform)\ 89 | ninja 90 | 91 | 92 | 93 | Level3 94 | Disabled 95 | true 96 | 97 | 98 | 99 | 100 | Level3 101 | Disabled 102 | true 103 | 104 | 105 | 106 | 107 | Level3 108 | MaxSpeed 109 | true 110 | true 111 | true 112 | 113 | 114 | true 115 | true 116 | 117 | 118 | 119 | 120 | Level3 121 | MaxSpeed 122 | true 123 | true 124 | true 125 | 126 | 127 | true 128 | true 129 | 130 | 131 | 132 | 133 | 134 | 135 | Document 136 | 137 | 138 | 139 | 140 | 141 | 142 | ninja 143 | Build shaders 144 | false 145 | ninja 146 | Build shaders 147 | false 148 | ninja 149 | Build shaders 150 | false 151 | ninja 152 | Build shaders 153 | false 154 | *spv* 155 | *spv* 156 | *spv* 157 | *spv* 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /Vulkan.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {EE3884AC-7E9E-4109-A512-92C650FDC095} 23 | Win32Proj 24 | Vulkan 25 | 8.1 26 | 27 | 28 | 29 | Application 30 | true 31 | v140 32 | MultiByte 33 | 34 | 35 | Application 36 | false 37 | v140 38 | true 39 | MultiByte 40 | 41 | 42 | Application 43 | true 44 | v140 45 | MultiByte 46 | 47 | 48 | Application 49 | false 50 | v140 51 | true 52 | MultiByte 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | $(VULKAN_SDK)\Include;$(IncludePath) 75 | $(VULKAN_SDK)\Lib32;$(LibraryPath) 76 | $(SolutionDir)build\temp\$(Configuration)_$(Platform)\ 77 | $(SolutionDir)build\bin_$(Configuration)_$(Platform)\ 78 | 79 | 80 | true 81 | $(VULKAN_SDK)\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 82 | $(VULKAN_SDK)\Lib;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64 83 | $(ProjectName)_x64 84 | $(SolutionDir)build\temp\$(Configuration)_$(Platform)\ 85 | $(SolutionDir)build\bin_$(Configuration)_$(Platform)\ 86 | 87 | 88 | false 89 | $(VULKAN_SDK)\Include;$(IncludePath) 90 | $(VULKAN_SDK)\Lib32;$(LibraryPath) 91 | $(SolutionDir)build\temp\$(Configuration)_$(Platform)\ 92 | $(SolutionDir)build\bin_$(Configuration)_$(Platform)\ 93 | 94 | 95 | false 96 | $(VULKAN_SDK)\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 97 | $(VULKAN_SDK)\Lib;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64 98 | $(ProjectName)_x64 99 | $(SolutionDir)build\temp\$(Configuration)_$(Platform)\ 100 | $(SolutionDir)build\bin_$(Configuration)_$(Platform)\ 101 | 102 | 103 | 104 | 105 | 106 | Level3 107 | Disabled 108 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 109 | true 110 | 111 | 112 | Console 113 | true 114 | sdl2.lib;sdl2main.lib;vulkan-1.lib;%(AdditionalDependencies) 115 | 116 | 117 | 118 | 119 | 120 | 121 | Level3 122 | Disabled 123 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | true 125 | 126 | 127 | Console 128 | true 129 | sdl2.lib;sdl2main.lib;vulkan-1.lib;%(AdditionalDependencies) 130 | 131 | 132 | 133 | 134 | Level3 135 | 136 | 137 | MaxSpeed 138 | true 139 | true 140 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | true 142 | 143 | 144 | Console 145 | true 146 | true 147 | true 148 | sdl2.lib;sdl2main.lib;vulkan-1.lib;%(AdditionalDependencies) 149 | 150 | 151 | 152 | 153 | Level3 154 | 155 | 156 | MaxSpeed 157 | true 158 | true 159 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 160 | true 161 | 162 | 163 | Console 164 | true 165 | true 166 | true 167 | sdl2.lib;sdl2main.lib;vulkan-1.lib;%(AdditionalDependencies) 168 | 169 | 170 | 171 | 172 | false 173 | false 174 | false 175 | false 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #define VULKAN_ENABLE_LUNARG_VALIDATION 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #define VK_USE_PLATFORM_WIN32_KHR 11 | #include 12 | 13 | #include "bits.h" 14 | 15 | enum { 16 | Kb = (1 << 10), 17 | Mb = (1 << 20), 18 | MAX_DEVICE_COUNT = 8, 19 | MAX_QUEUE_COUNT = 4, //ATM there should be at most transfer, graphics, compute, graphics+compute families 20 | MAX_PRESENT_MODE_COUNT = 6, // At the moment in spec 21 | MAX_SWAPCHAIN_IMAGES = 3, 22 | FRAME_COUNT = 2, 23 | PRESENT_MODE_MAILBOX_IMAGE_COUNT = 3, 24 | PRESENT_MODE_DEFAULT_IMAGE_COUNT = 2, 25 | UPLOAD_REGION_SIZE = 64 * Kb, 26 | UPLOAD_BUFFER_SIZE = FRAME_COUNT * UPLOAD_REGION_SIZE, 27 | STATIC_BUFFER_SIZE = 64 * Kb, 28 | }; 29 | 30 | enum { 31 | VULKAN_MEM_DEVICE_READBACK, 32 | VULKAN_MEM_DEVICE_UPLOAD, 33 | VULKAN_MEM_DEVICE_LOCAL, 34 | VULKAN_MEM_COUNT 35 | }; 36 | 37 | static uint32_t clamp_u32(uint32_t value, uint32_t min, uint32_t max) 38 | { 39 | return value < min ? min : (value > max ? max : value); 40 | } 41 | 42 | typedef struct tagVertexP2C 43 | { 44 | float x, y; 45 | uint32_t rgba; 46 | } VertexP2C; 47 | 48 | //---------------------------------------------------------- 49 | 50 | const VkApplicationInfo appInfo = { 51 | .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, 52 | .pApplicationName = "Vulkan SDL tutorial", 53 | .applicationVersion = VK_MAKE_VERSION(1, 0, 0), 54 | .pEngineName = "No Engine", 55 | .engineVersion = VK_MAKE_VERSION(1, 0, 0), 56 | .apiVersion = VK_API_VERSION_1_0, 57 | }; 58 | 59 | const VkInstanceCreateInfo createInfo = { 60 | .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 61 | .pApplicationInfo = &appInfo, 62 | .enabledExtensionCount = 2, 63 | .ppEnabledExtensionNames = (const char* const[]) { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME }, 64 | }; 65 | 66 | #ifdef VULKAN_ENABLE_LUNARG_VALIDATION 67 | const VkInstanceCreateInfo createInfoLunarGValidation = { 68 | .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 69 | .pApplicationInfo = &appInfo, 70 | .enabledLayerCount = 1, 71 | .ppEnabledLayerNames = (const char* const[]) { "VK_LAYER_LUNARG_standard_validation" }, 72 | .enabledExtensionCount = 3, 73 | .ppEnabledExtensionNames = (const char* const[]) { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_EXTENSION_NAME }, 74 | }; 75 | 76 | PFN_vkCreateDebugReportCallbackEXT vkpfn_CreateDebugReportCallbackEXT = 0; 77 | PFN_vkDestroyDebugReportCallbackEXT vkpfn_DestroyDebugReportCallbackEXT = 0; 78 | 79 | VkDebugReportCallbackEXT debugCallback = VK_NULL_HANDLE; 80 | 81 | static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanReportFunc( 82 | VkDebugReportFlagsEXT flags, 83 | VkDebugReportObjectTypeEXT objType, 84 | uint64_t obj, 85 | size_t location, 86 | int32_t code, 87 | const char* layerPrefix, 88 | const char* msg, 89 | void* userData) 90 | { 91 | printf("VULKAN VALIDATION: %s\n", msg); 92 | return VK_FALSE; 93 | } 94 | 95 | VkDebugReportCallbackCreateInfoEXT debugCallbackCreateInfo = { 96 | .sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, 97 | .flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, 98 | .pfnCallback = VulkanReportFunc, 99 | }; 100 | #endif 101 | 102 | VkInstance instance = VK_NULL_HANDLE; 103 | 104 | int init_vulkan() 105 | { 106 | VkResult result = VK_ERROR_INITIALIZATION_FAILED; 107 | #ifdef VULKAN_ENABLE_LUNARG_VALIDATION 108 | result = vkCreateInstance(&createInfoLunarGValidation, 0, &instance); 109 | if (result == VK_SUCCESS) 110 | { 111 | vkpfn_CreateDebugReportCallbackEXT = 112 | (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); 113 | vkpfn_DestroyDebugReportCallbackEXT = 114 | (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); 115 | if (vkpfn_CreateDebugReportCallbackEXT && vkpfn_DestroyDebugReportCallbackEXT) 116 | { 117 | vkpfn_CreateDebugReportCallbackEXT(instance, &debugCallbackCreateInfo, 0, &debugCallback); 118 | } 119 | } 120 | #endif 121 | if (result != VK_SUCCESS) 122 | { 123 | result = vkCreateInstance(&createInfo, 0, &instance); 124 | } 125 | 126 | return result == VK_SUCCESS; 127 | } 128 | 129 | void fini_vulkan() 130 | { 131 | #ifdef VULKAN_ENABLE_LUNARG_VALIDATION 132 | if (vkpfn_DestroyDebugReportCallbackEXT && debugCallback) 133 | { 134 | vkpfn_DestroyDebugReportCallbackEXT(instance, debugCallback, 0); 135 | } 136 | #endif 137 | vkDestroyInstance(instance, 0); 138 | } 139 | 140 | //---------------------------------------------------------- 141 | 142 | SDL_Window* window = 0; 143 | VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR }; 144 | VkSurfaceKHR surface = VK_NULL_HANDLE; 145 | uint32_t width = 1280; 146 | uint32_t height = 720; 147 | 148 | int init_window() 149 | { 150 | SDL_Window* window = SDL_CreateWindow( 151 | "Vulkan Sample", 152 | SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 153 | width, height, 154 | 0 155 | ); 156 | 157 | SDL_SysWMinfo info; 158 | SDL_VERSION(&info.version); 159 | SDL_GetWindowWMInfo(window, &info); 160 | 161 | surfaceCreateInfo.hinstance = GetModuleHandle(0); 162 | surfaceCreateInfo.hwnd = info.info.win.window; 163 | 164 | VkResult result = vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface); 165 | 166 | return window != 0 && result == VK_SUCCESS; 167 | } 168 | 169 | void fini_window() 170 | { 171 | vkDestroySurfaceKHR(instance, surface, 0); 172 | SDL_DestroyWindow(window); 173 | } 174 | 175 | //---------------------------------------------------------- 176 | 177 | VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; 178 | uint32_t queueFamilyIndex; 179 | VkQueue queue; 180 | VkDevice device = VK_NULL_HANDLE; 181 | VkPhysicalDeviceMemoryProperties deviceMemProperties; 182 | uint32_t compatibleMemTypes[VULKAN_MEM_COUNT]; 183 | 184 | uint32_t vkutFindCompatibleMemoryType(VkPhysicalDeviceMemoryProperties* memProperties, VkMemoryPropertyFlags flags) 185 | { 186 | const uint32_t count = memProperties->memoryTypeCount; 187 | uint32_t compatibleMemoryTypes = 0; 188 | for (uint32_t i = 0; i < count; i++) 189 | { 190 | int isCompatible = ( memProperties->memoryTypes[i].propertyFlags & flags) == flags; 191 | compatibleMemoryTypes |= (isCompatible << i); 192 | } 193 | 194 | return compatibleMemoryTypes; 195 | } 196 | 197 | VkDeviceCreateInfo deviceCreateInfo = { 198 | .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 199 | .enabledExtensionCount = 1, 200 | .ppEnabledExtensionNames = (const char* const[]) { VK_KHR_SWAPCHAIN_EXTENSION_NAME }, 201 | }; 202 | 203 | int init_device() 204 | { 205 | uint32_t physicalDeviceCount; 206 | VkPhysicalDevice deviceHandles[MAX_DEVICE_COUNT]; 207 | VkQueueFamilyProperties queueFamilyProperties[MAX_QUEUE_COUNT]; 208 | 209 | vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, 0); 210 | physicalDeviceCount = physicalDeviceCount > MAX_DEVICE_COUNT ? MAX_DEVICE_COUNT : physicalDeviceCount; 211 | vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, deviceHandles); 212 | 213 | for (uint32_t i = 0; i < physicalDeviceCount; ++i) 214 | { 215 | uint32_t queueFamilyCount = 0; 216 | vkGetPhysicalDeviceQueueFamilyProperties(deviceHandles[i], &queueFamilyCount, NULL); 217 | queueFamilyCount = queueFamilyCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueFamilyCount; 218 | vkGetPhysicalDeviceQueueFamilyProperties(deviceHandles[i], &queueFamilyCount, queueFamilyProperties); 219 | 220 | for (uint32_t j = 0; j < queueFamilyCount; ++j) { 221 | 222 | VkBool32 supportsPresent = VK_FALSE; 223 | vkGetPhysicalDeviceSurfaceSupportKHR(deviceHandles[i], j, surface, &supportsPresent); 224 | 225 | if (supportsPresent && (queueFamilyProperties[j].queueFlags & VK_QUEUE_GRAPHICS_BIT)) 226 | { 227 | queueFamilyIndex = j; 228 | physicalDevice = deviceHandles[i]; 229 | break; 230 | } 231 | } 232 | 233 | if (physicalDevice) 234 | { 235 | break; 236 | } 237 | } 238 | 239 | deviceCreateInfo.queueCreateInfoCount = 1, 240 | deviceCreateInfo.pQueueCreateInfos = (VkDeviceQueueCreateInfo[]) { 241 | { 242 | .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 243 | .queueFamilyIndex = queueFamilyIndex, 244 | .queueCount = 1, 245 | .pQueuePriorities = (const float[]) { 1.0f } 246 | } 247 | }; 248 | VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, NULL, &device); 249 | 250 | vkGetDeviceQueue(device, queueFamilyIndex, 0, &queue); 251 | 252 | vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemProperties); 253 | compatibleMemTypes[VULKAN_MEM_DEVICE_READBACK] 254 | = vkutFindCompatibleMemoryType(&deviceMemProperties, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT 255 | | VK_MEMORY_PROPERTY_HOST_CACHED_BIT); 256 | compatibleMemTypes[VULKAN_MEM_DEVICE_UPLOAD] 257 | = vkutFindCompatibleMemoryType(&deviceMemProperties, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT 258 | | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); 259 | compatibleMemTypes[VULKAN_MEM_DEVICE_LOCAL] 260 | = vkutFindCompatibleMemoryType(&deviceMemProperties, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); 261 | 262 | return result == VK_SUCCESS; 263 | } 264 | 265 | void fini_device() 266 | { 267 | vkDestroyDevice(device, 0); 268 | } 269 | 270 | //---------------------------------------------------------- 271 | 272 | VkSwapchainKHR swapchain; 273 | uint32_t swapchainImageCount; 274 | VkImage swapchainImages[MAX_SWAPCHAIN_IMAGES]; 275 | VkExtent2D swapchainExtent; 276 | VkSurfaceFormatKHR surfaceFormat; 277 | 278 | int init_swapchain() 279 | { 280 | //Use first available format 281 | uint32_t formatCount = 1; 282 | vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, 0); // suppress validation layer 283 | vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, &surfaceFormat); 284 | surfaceFormat.format = surfaceFormat.format == VK_FORMAT_UNDEFINED ? VK_FORMAT_B8G8R8A8_UNORM : surfaceFormat.format; 285 | 286 | uint32_t presentModeCount = 0; 287 | vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL); 288 | VkPresentModeKHR presentModes[MAX_PRESENT_MODE_COUNT]; 289 | presentModeCount = presentModeCount > MAX_PRESENT_MODE_COUNT ? MAX_PRESENT_MODE_COUNT : presentModeCount; 290 | vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes); 291 | 292 | VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; // always supported. 293 | for (uint32_t i = 0; i < presentModeCount; ++i) 294 | { 295 | if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) 296 | { 297 | presentMode = VK_PRESENT_MODE_MAILBOX_KHR; 298 | break; 299 | } 300 | } 301 | swapchainImageCount = presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? PRESENT_MODE_MAILBOX_IMAGE_COUNT : PRESENT_MODE_DEFAULT_IMAGE_COUNT; 302 | 303 | VkSurfaceCapabilitiesKHR surfaceCapabilities; 304 | vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities); 305 | 306 | swapchainExtent = surfaceCapabilities.currentExtent; 307 | if (swapchainExtent.width == UINT32_MAX) 308 | { 309 | swapchainExtent.width = clamp_u32(width, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width); 310 | swapchainExtent.height = clamp_u32(height, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height); 311 | } 312 | 313 | VkSwapchainCreateInfoKHR swapChainCreateInfo = { 314 | .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, 315 | .surface = surface, 316 | .minImageCount = swapchainImageCount, 317 | .imageFormat = surfaceFormat.format, 318 | .imageColorSpace = surfaceFormat.colorSpace, 319 | .imageExtent = swapchainExtent, 320 | .imageArrayLayers = 1, // 2 for stereo 321 | .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 322 | .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, 323 | .preTransform = surfaceCapabilities.currentTransform, 324 | .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 325 | .presentMode = presentMode, 326 | .clipped = VK_TRUE, 327 | }; 328 | 329 | VkResult result = vkCreateSwapchainKHR(device, &swapChainCreateInfo, 0, &swapchain); 330 | if (result != VK_SUCCESS) 331 | { 332 | return 0; 333 | } 334 | 335 | vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, NULL); 336 | vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, swapchainImages); 337 | 338 | return 1; 339 | } 340 | 341 | void fini_swapchain() 342 | { 343 | vkDestroySwapchainKHR(device, swapchain, 0); 344 | } 345 | 346 | //---------------------------------------------------------- 347 | 348 | VkRenderPass renderPass; 349 | 350 | int createRenderPass() 351 | { 352 | VkRenderPassCreateInfo renderPassCreateInfo = { 353 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, 354 | .attachmentCount = 1, 355 | .pAttachments = &(VkAttachmentDescription) { 356 | .format = surfaceFormat.format, 357 | .samples = VK_SAMPLE_COUNT_1_BIT, 358 | .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, 359 | .storeOp = VK_ATTACHMENT_STORE_OP_STORE, 360 | .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, 361 | .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, 362 | .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, 363 | .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 364 | }, 365 | .subpassCount = 1, 366 | .pSubpasses = &(VkSubpassDescription) { 367 | .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, 368 | .colorAttachmentCount = 1, 369 | .pColorAttachments = &(VkAttachmentReference) { 370 | .attachment = 0, 371 | .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 372 | }, 373 | }, 374 | }; 375 | 376 | vkCreateRenderPass(device, &renderPassCreateInfo, 0, &renderPass); 377 | 378 | return 1; 379 | } 380 | 381 | void destroyRenderPass() 382 | { 383 | vkDestroyRenderPass(device, renderPass, NULL); 384 | } 385 | 386 | VkImageView swapchainImageViews[MAX_SWAPCHAIN_IMAGES]; 387 | VkFramebuffer framebuffers[MAX_SWAPCHAIN_IMAGES]; 388 | 389 | int createFramebuffers() 390 | { 391 | for (uint32_t i = 0; i < swapchainImageCount; ++i) 392 | { 393 | VkImageViewCreateInfo createInfo = { 394 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 395 | .image = swapchainImages[i], 396 | .viewType = VK_IMAGE_VIEW_TYPE_2D, 397 | .format = surfaceFormat.format, 398 | .subresourceRange = { 399 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 400 | .baseMipLevel = 0, 401 | .levelCount = 1, 402 | .baseArrayLayer = 0, 403 | .layerCount = 1, 404 | }, 405 | }; 406 | vkCreateImageView(device, &createInfo, 0, &swapchainImageViews[i]); 407 | 408 | VkFramebufferCreateInfo framebufferCreateInfo = { 409 | .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, 410 | .renderPass = renderPass, 411 | .attachmentCount = 1, 412 | .pAttachments = &swapchainImageViews[i], 413 | .width = swapchainExtent.width, 414 | .height = swapchainExtent.height, 415 | .layers = 1, 416 | }; 417 | vkCreateFramebuffer(device, &framebufferCreateInfo, 0, &framebuffers[i]); 418 | } 419 | 420 | return 1; 421 | } 422 | 423 | void destroyFramebuffers() 424 | { 425 | for (uint32_t i = 0; i < swapchainImageCount; ++i) 426 | { 427 | vkDestroyFramebuffer(device, framebuffers[i], NULL); 428 | vkDestroyImageView(device, swapchainImageViews[i], NULL); 429 | } 430 | } 431 | 432 | VkShaderModule createShaderModule(const char* shaderFile) 433 | { 434 | VkShaderModule shaderModule = VK_NULL_HANDLE; 435 | 436 | HANDLE hFile = CreateFile(shaderFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); 437 | if (hFile == INVALID_HANDLE_VALUE) return VK_NULL_HANDLE; 438 | 439 | LARGE_INTEGER size; 440 | GetFileSizeEx(hFile, &size); 441 | 442 | HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); 443 | CloseHandle(hFile); 444 | if (!hMapping) return VK_NULL_HANDLE; 445 | 446 | const uint32_t* data = (const uint32_t*)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); 447 | 448 | VkShaderModuleCreateInfo shaderModuleCreateInfo = { 449 | .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, 450 | .codeSize = size.LowPart, 451 | .pCode = data, 452 | }; 453 | vkCreateShaderModule(device, &shaderModuleCreateInfo, 0, &shaderModule); 454 | 455 | UnmapViewOfFile(data); 456 | CloseHandle(hMapping); 457 | 458 | return shaderModule; 459 | } 460 | 461 | VkPipelineLayout pipelineLayout; 462 | VkPipeline pipeline; 463 | 464 | int createPipeline() 465 | { 466 | VkShaderModule vertexShader = createShaderModule("shaders\\vertex_color.spv-vs"); 467 | VkShaderModule fragmentShader = createShaderModule("shaders\\vertex_color.spv-fs"); 468 | 469 | const VkPipelineShaderStageCreateInfo stages[] = { 470 | { 471 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 472 | .stage = VK_SHADER_STAGE_VERTEX_BIT, 473 | .module = vertexShader, 474 | .pName = "main", 475 | }, 476 | { 477 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 478 | .stage = VK_SHADER_STAGE_FRAGMENT_BIT, 479 | .module = fragmentShader, 480 | .pName = "main", 481 | }, 482 | }; 483 | VkPipelineVertexInputStateCreateInfo vertexInputState = { 484 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, 485 | .vertexBindingDescriptionCount = 1, 486 | .pVertexBindingDescriptions = (VkVertexInputBindingDescription[]) { 487 | {.binding = 0, .stride = sizeof(VertexP2C), .inputRate = VK_VERTEX_INPUT_RATE_VERTEX} 488 | }, 489 | .vertexAttributeDescriptionCount = 2, 490 | .pVertexAttributeDescriptions = (VkVertexInputAttributeDescription[]) { 491 | {.location = 0, .binding = 0, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(VertexP2C, x)}, 492 | {.location = 1, .binding = 0, .format = VK_FORMAT_R8G8B8A8_UNORM, .offset = offsetof(VertexP2C, rgba)} 493 | }, 494 | }; 495 | VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = { 496 | .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, 497 | .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 498 | .primitiveRestartEnable = VK_FALSE, 499 | }; 500 | VkPipelineRasterizationStateCreateInfo rasterizationState = { 501 | .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, 502 | .depthClampEnable = VK_FALSE, 503 | .rasterizerDiscardEnable = VK_FALSE, 504 | .polygonMode = VK_POLYGON_MODE_FILL, 505 | .cullMode = VK_CULL_MODE_BACK_BIT, 506 | .frontFace = VK_FRONT_FACE_CLOCKWISE, 507 | .depthBiasEnable = VK_FALSE, 508 | .depthBiasConstantFactor = 0.0f, 509 | .depthBiasClamp = 0.0f, 510 | .depthBiasSlopeFactor = 0.0f, 511 | .lineWidth = 1.0f, 512 | }; 513 | VkPipelineViewportStateCreateInfo viewportState = { 514 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, 515 | .viewportCount = 1, 516 | .pViewports = 0, 517 | .scissorCount = 1, 518 | .pScissors = 0, 519 | }; 520 | VkPipelineMultisampleStateCreateInfo multisampleState = { 521 | .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, 522 | .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, 523 | .sampleShadingEnable = VK_FALSE, 524 | .minSampleShading = 1.0f, 525 | .pSampleMask = 0, 526 | .alphaToCoverageEnable = VK_FALSE, 527 | .alphaToOneEnable = VK_FALSE, 528 | }; 529 | VkPipelineColorBlendStateCreateInfo colorBlendState = { 530 | .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 531 | .logicOpEnable = VK_FALSE, 532 | .attachmentCount = 1, 533 | .pAttachments = (VkPipelineColorBlendAttachmentState[]) { 534 | { 535 | .blendEnable = VK_FALSE, 536 | .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, 537 | .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, 538 | .colorBlendOp = VK_BLEND_OP_ADD, 539 | .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, 540 | .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, 541 | .alphaBlendOp = VK_BLEND_OP_ADD, 542 | .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, 543 | }, 544 | }, 545 | }; 546 | VkPipelineDynamicStateCreateInfo dynamicState = { 547 | .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, 548 | .dynamicStateCount = 2, 549 | .pDynamicStates = (VkDynamicState[]) { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }, 550 | }; 551 | 552 | VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { 553 | .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 554 | }; 555 | vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, 0, &pipelineLayout); 556 | 557 | VkGraphicsPipelineCreateInfo pipelineCreateInfo = { 558 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, 559 | .stageCount = 2, 560 | .pStages = stages, 561 | .pVertexInputState = &vertexInputState, 562 | .pInputAssemblyState = &inputAssemblyState, 563 | .pViewportState = &viewportState, 564 | .pRasterizationState = &rasterizationState, 565 | .pMultisampleState = &multisampleState , 566 | .pColorBlendState = &colorBlendState, 567 | .pDynamicState = &dynamicState, 568 | .layout = pipelineLayout, 569 | .renderPass = renderPass, 570 | }; 571 | 572 | vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, 0, &pipeline); 573 | 574 | vkDestroyShaderModule(device, vertexShader, 0); 575 | vkDestroyShaderModule(device, fragmentShader, 0); 576 | 577 | return pipeline != 0; 578 | } 579 | 580 | void destroyPipeline() 581 | { 582 | vkDestroyPipeline(device, pipeline, 0); 583 | vkDestroyPipelineLayout(device, pipelineLayout, 0); 584 | } 585 | 586 | VkBuffer uploadBuffer; 587 | VkDeviceMemory uploadBufferMemory; 588 | void* uploadBufferPtr; 589 | VkBuffer staticBuffer; 590 | VkDeviceMemory staticBufferMemory; 591 | 592 | int createUploadBuffer() 593 | { 594 | VkBufferCreateInfo bufferCreateInfo; 595 | VkMemoryAllocateInfo allocInfo; 596 | VkMemoryRequirements memoryRequirements; 597 | uint32_t memoryIndex; 598 | 599 | bufferCreateInfo = (VkBufferCreateInfo){ 600 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 601 | .size = UPLOAD_BUFFER_SIZE, 602 | .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT 603 | | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT 604 | | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, 605 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 606 | .queueFamilyIndexCount = 1, 607 | .pQueueFamilyIndices = &queueFamilyIndex, 608 | }; 609 | vkCreateBuffer(device, &bufferCreateInfo, NULL, &uploadBuffer); 610 | vkGetBufferMemoryRequirements(device, uploadBuffer, &memoryRequirements); 611 | 612 | memoryIndex = bit_ffs32(compatibleMemTypes[VULKAN_MEM_DEVICE_UPLOAD] & memoryRequirements.memoryTypeBits); 613 | allocInfo = (VkMemoryAllocateInfo){ 614 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 615 | .allocationSize = memoryRequirements.size, 616 | .memoryTypeIndex = memoryIndex, 617 | }; 618 | vkAllocateMemory(device, &allocInfo, NULL, &uploadBufferMemory); 619 | vkBindBufferMemory(device, uploadBuffer, uploadBufferMemory, 0); 620 | vkMapMemory(device, uploadBufferMemory, 0, VK_WHOLE_SIZE, 0, &uploadBufferPtr); 621 | 622 | bufferCreateInfo = (VkBufferCreateInfo){ 623 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 624 | .size = STATIC_BUFFER_SIZE, 625 | .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT 626 | | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT 627 | | VK_BUFFER_USAGE_TRANSFER_DST_BIT, 628 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 629 | .queueFamilyIndexCount = 1, 630 | .pQueueFamilyIndices = &queueFamilyIndex, 631 | }; 632 | vkCreateBuffer(device, &bufferCreateInfo, NULL, &staticBuffer); 633 | vkGetBufferMemoryRequirements(device, staticBuffer, &memoryRequirements); 634 | 635 | memoryIndex = bit_ffs32(compatibleMemTypes[VULKAN_MEM_DEVICE_LOCAL] & memoryRequirements.memoryTypeBits); 636 | allocInfo = (VkMemoryAllocateInfo){ 637 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 638 | .allocationSize = memoryRequirements.size, 639 | .memoryTypeIndex = memoryIndex, 640 | }; 641 | vkAllocateMemory(device, &allocInfo, NULL, &staticBufferMemory); 642 | vkBindBufferMemory(device, staticBuffer, staticBufferMemory, 0); 643 | 644 | return TRUE; 645 | } 646 | 647 | void destroyUploadBuffer() 648 | { 649 | vkFreeMemory(device, staticBufferMemory, NULL); 650 | vkDestroyBuffer(device, staticBuffer, NULL); 651 | 652 | vkFreeMemory(device, uploadBufferMemory, NULL); 653 | vkDestroyBuffer(device, uploadBuffer, NULL); 654 | } 655 | 656 | uint32_t frameIndex = 0; 657 | VkCommandPool commandPool; 658 | VkCommandBuffer commandBuffers[FRAME_COUNT]; 659 | VkFence frameFences[FRAME_COUNT]; // Create with VK_FENCE_CREATE_SIGNALED_BIT. 660 | VkSemaphore imageAvailableSemaphores[FRAME_COUNT]; 661 | VkSemaphore renderFinishedSemaphores[FRAME_COUNT]; 662 | 663 | int init_render() 664 | { 665 | VkCommandPoolCreateInfo commandPoolCreateInfo = { 666 | .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 667 | .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, 668 | .queueFamilyIndex = queueFamilyIndex, 669 | }; 670 | 671 | vkCreateCommandPool(device, &commandPoolCreateInfo, 0, &commandPool); 672 | 673 | VkCommandBufferAllocateInfo commandBufferAllocInfo = { 674 | .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 675 | .commandPool = commandPool, 676 | .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, 677 | .commandBufferCount = FRAME_COUNT, 678 | }; 679 | 680 | vkAllocateCommandBuffers(device, &commandBufferAllocInfo, commandBuffers); 681 | 682 | VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; 683 | 684 | vkCreateSemaphore(device, &semaphoreCreateInfo, 0, &imageAvailableSemaphores[0]); 685 | vkCreateSemaphore(device, &semaphoreCreateInfo, 0, &imageAvailableSemaphores[1]); 686 | vkCreateSemaphore(device, &semaphoreCreateInfo, 0, &renderFinishedSemaphores[0]); 687 | vkCreateSemaphore(device, &semaphoreCreateInfo, 0, &renderFinishedSemaphores[1]); 688 | 689 | VkFenceCreateInfo fenceCreateInfo = { 690 | .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, 691 | .flags = VK_FENCE_CREATE_SIGNALED_BIT 692 | }; 693 | 694 | vkCreateFence(device, &fenceCreateInfo, 0, &frameFences[0]); 695 | vkCreateFence(device, &fenceCreateInfo, 0, &frameFences[1]); 696 | 697 | createRenderPass(); 698 | createFramebuffers(); 699 | createPipeline(); 700 | createUploadBuffer(); 701 | 702 | return 1; 703 | } 704 | 705 | void fini_render() 706 | { 707 | vkDeviceWaitIdle(device); 708 | destroyUploadBuffer(); 709 | destroyPipeline(); 710 | destroyFramebuffers(); 711 | destroyRenderPass(); 712 | vkDestroyFence(device, frameFences[0], 0); 713 | vkDestroyFence(device, frameFences[1], 0); 714 | vkDestroySemaphore(device, renderFinishedSemaphores[0], 0); 715 | vkDestroySemaphore(device, renderFinishedSemaphores[1], 0); 716 | vkDestroySemaphore(device, imageAvailableSemaphores[0], 0); 717 | vkDestroySemaphore(device, imageAvailableSemaphores[1], 0); 718 | vkDestroyCommandPool(device, commandPool, 0); 719 | } 720 | 721 | void draw_frame() 722 | { 723 | uint32_t index = frameIndex % FRAME_COUNT; 724 | vkWaitForFences(device, 1, &frameFences[index], VK_TRUE, UINT64_MAX); 725 | vkResetFences(device, 1, &frameFences[index]); 726 | 727 | size_t uploadOffset = index * UPLOAD_REGION_SIZE; 728 | size_t uploadLimit = uploadOffset + UPLOAD_REGION_SIZE; 729 | uint8_t* uploadPtr = (uint8_t*)uploadBufferPtr; 730 | 731 | uint32_t mask = (SDL_GetTicks() >> 3) & 0x1FF; 732 | mask = mask > 0xFF ? 0x1FF - mask : mask; 733 | mask = (mask << 16) | (mask << 8) | mask; 734 | VertexP2C dynamicVertices[] = { 735 | { -0.5f, -1.0f, 0xFF0000FF|mask }, 736 | { 0.0f, 0.0f, 0xFF00FF00|mask }, 737 | { -1.0f, 0.0f, 0xFFFF0000|mask } 738 | }; 739 | memcpy(uploadPtr+uploadOffset, dynamicVertices, sizeof(dynamicVertices)); 740 | uploadOffset += sizeof(dynamicVertices); 741 | 742 | VkBufferCopy bufferCopyInfo; 743 | if (frameIndex == 0) 744 | { 745 | VertexP2C staticVertices[] = { 746 | { 0.5f, 0.0f, 0xFF0000FF }, 747 | { 1.0f, 1.0f, 0xFF00FF00 }, 748 | { 0.0f, 1.0f, 0xFFFF0000 } 749 | }; 750 | memcpy(uploadPtr + uploadOffset, staticVertices, sizeof(staticVertices)); 751 | bufferCopyInfo = (VkBufferCopy){ 752 | .srcOffset = uploadOffset, 753 | .dstOffset = 0, 754 | .size = sizeof(staticVertices), 755 | }; 756 | } 757 | 758 | uint32_t imageIndex; 759 | vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, imageAvailableSemaphores[index], VK_NULL_HANDLE, &imageIndex); 760 | 761 | VkCommandBufferBeginInfo beginInfo = { 762 | .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 763 | .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT 764 | }; 765 | vkBeginCommandBuffer(commandBuffers[index], &beginInfo); 766 | 767 | if (frameIndex == 0) 768 | { 769 | vkCmdCopyBuffer(commandBuffers[index], uploadBuffer, staticBuffer, 1, &bufferCopyInfo); 770 | vkCmdPipelineBarrier(commandBuffers[index], 771 | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, 772 | 1, &(VkMemoryBarrier){ 773 | .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, 774 | .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, 775 | .dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT 776 | }, 777 | 0, NULL, 0, NULL 778 | ); 779 | } 780 | 781 | vkCmdBeginRenderPass(commandBuffers[index], 782 | &(VkRenderPassBeginInfo) { 783 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, 784 | .renderPass = renderPass, 785 | .framebuffer = framebuffers[imageIndex], 786 | .clearValueCount = 1, 787 | .pClearValues = &(VkClearValue) { 0.0f, 0.1f, 0.2f, 1.0f }, 788 | .renderArea.offset = (VkOffset2D) { .x = 0,.y = 0 }, 789 | .renderArea.extent = swapchainExtent, 790 | }, 791 | VK_SUBPASS_CONTENTS_INLINE 792 | ); 793 | 794 | vkCmdBindPipeline(commandBuffers[index], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); 795 | vkCmdSetViewport(commandBuffers[index], 0, 1, &(VkViewport){ 0.0f, 0.0f, (float)swapchainExtent.width, (float)swapchainExtent.height, 0.0f, 1.0f}); 796 | vkCmdSetScissor(commandBuffers[index], 0, 1, &(VkRect2D){ {0, 0}, swapchainExtent}); 797 | 798 | vkCmdBindVertexBuffers(commandBuffers[index], 0, 1, &uploadBuffer, (VkDeviceSize[]) { 0 }); 799 | vkCmdDraw(commandBuffers[index], 3, 1, 0, 0); 800 | 801 | vkCmdBindVertexBuffers(commandBuffers[index], 0, 1, &staticBuffer, (VkDeviceSize[]) { 0 }); 802 | vkCmdDraw(commandBuffers[index], 3, 1, 0, 0); 803 | 804 | vkCmdEndRenderPass(commandBuffers[index]); 805 | 806 | vkEndCommandBuffer(commandBuffers[index]); 807 | 808 | VkSubmitInfo submitInfo = { 809 | .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, 810 | .waitSemaphoreCount = 1, 811 | .pWaitSemaphores = &imageAvailableSemaphores[index], 812 | .pWaitDstStageMask = (VkPipelineStageFlags[]) { VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT }, 813 | .commandBufferCount = 1, 814 | .pCommandBuffers = &commandBuffers[index], 815 | .signalSemaphoreCount = 1, 816 | .pSignalSemaphores = &renderFinishedSemaphores[index], 817 | }; 818 | vkQueueSubmit(queue, 1, &submitInfo, frameFences[index]); 819 | 820 | VkPresentInfoKHR presentInfo = { 821 | .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 822 | .waitSemaphoreCount = 1, 823 | .pWaitSemaphores = &renderFinishedSemaphores[index], 824 | .swapchainCount = 1, 825 | .pSwapchains = &swapchain, 826 | .pImageIndices = &imageIndex, 827 | }; 828 | vkQueuePresentKHR(queue, &presentInfo); 829 | 830 | ++frameIndex; 831 | } 832 | 833 | //---------------------------------------------------------- 834 | 835 | int main(int argc, char *argv[]) 836 | { 837 | SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); 838 | 839 | int run = init_vulkan() && init_window() && init_device() && init_swapchain() && init_render(); 840 | while (run) 841 | { 842 | SDL_Event evt; 843 | while (SDL_PollEvent(&evt)) 844 | { 845 | if (evt.type == SDL_QUIT) 846 | { 847 | run = 0; 848 | } 849 | } 850 | 851 | draw_frame(); 852 | } 853 | 854 | fini_render(); 855 | fini_swapchain(); 856 | fini_device(); 857 | fini_window(); 858 | fini_vulkan(); 859 | 860 | SDL_Quit(); 861 | 862 | return 0; 863 | } 864 | --------------------------------------------------------------------------------