├── 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 |
--------------------------------------------------------------------------------