source: opengl-game/vulkan-game.cpp@ 1add0ed

feature/imgui-sdl points-test
Last change on this file since 1add0ed was 1add0ed, checked in by Dmitry Portnoy <dmitry.portnoy@…>, 4 years ago

Set model_base and call updateObject()to control the initial position of the textured squares instead of manually changing the z-coordinate of their points

  • Property mode set to 100644
File size: 60.5 KB
Line 
1#include "vulkan-game.hpp"
2
3#include <array>
4#include <iostream>
5#include <set>
6
7#include "consts.hpp"
8#include "logger.hpp"
9
10#include "utils.hpp"
11
12using namespace std;
13
14VulkanGame::VulkanGame(int maxFramesInFlight) : MAX_FRAMES_IN_FLIGHT(maxFramesInFlight) {
15 gui = nullptr;
16 window = nullptr;
17 font = nullptr;
18 fontSDLTexture = nullptr;
19 imageSDLTexture = nullptr;
20
21 currentFrame = 0;
22 framebufferResized = false;
23
24 object_VP_mats = {};
25 ship_VP_mats = {};
26 asteroid_VP_mats = {};
27}
28
29VulkanGame::~VulkanGame() {
30}
31
32void VulkanGame::run(int width, int height, unsigned char guiFlags) {
33 seedRandomNums();
34
35 cout << "DEBUGGING IS " << (ENABLE_VALIDATION_LAYERS ? "ON" : "OFF") << endl;
36
37 cout << "Vulkan Game" << endl;
38
39 // This gets the runtime version, use SDL_VERSION() for the comppile-time version
40 // TODO: Create a game-gui function to get the gui version and retrieve it that way
41 SDL_GetVersion(&sdlVersion);
42
43 // TODO: Refactor the logger api to be more flexible,
44 // esp. since gl_log() and gl_log_err() have issues printing anything besides stirngs
45 restart_gl_log();
46 gl_log("starting SDL\n%s.%s.%s",
47 to_string(sdlVersion.major).c_str(),
48 to_string(sdlVersion.minor).c_str(),
49 to_string(sdlVersion.patch).c_str());
50
51 open_log();
52 get_log() << "starting SDL" << endl;
53 get_log() <<
54 (int)sdlVersion.major << "." <<
55 (int)sdlVersion.minor << "." <<
56 (int)sdlVersion.patch << endl;
57
58 if (initWindow(width, height, guiFlags) == RTWO_ERROR) {
59 return;
60 }
61
62 initVulkan();
63 mainLoop();
64 cleanup();
65
66 close_log();
67}
68
69// TODO: Make some more init functions, or call this initUI if the
70// amount of things initialized here keeps growing
71bool VulkanGame::initWindow(int width, int height, unsigned char guiFlags) {
72 // TODO: Put all fonts, textures, and images in the assets folder
73 gui = new GameGui_SDL();
74
75 if (gui->init() == RTWO_ERROR) {
76 // TODO: Also print these sorts of errors to the log
77 cout << "UI library could not be initialized!" << endl;
78 cout << gui->getError() << endl;
79 return RTWO_ERROR;
80 }
81
82 window = (SDL_Window*) gui->createWindow("Vulkan Game", width, height, guiFlags & GUI_FLAGS_WINDOW_FULLSCREEN);
83 if (window == nullptr) {
84 cout << "Window could not be created!" << endl;
85 cout << gui->getError() << endl;
86 return RTWO_ERROR;
87 }
88
89 cout << "Target window size: (" << width << ", " << height << ")" << endl;
90 cout << "Actual window size: (" << gui->getWindowWidth() << ", " << gui->getWindowHeight() << ")" << endl;
91
92 renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
93 if (renderer == nullptr) {
94 cout << "Renderer could not be created!" << endl;
95 cout << gui->getError() << endl;
96 return RTWO_ERROR;
97 }
98
99 SDL_VERSION(&sdlVersion);
100
101 cout << "SDL "<<
102 to_string(sdlVersion.major) << "." <<
103 to_string(sdlVersion.minor) << "." <<
104 to_string(sdlVersion.patch) << endl;
105
106 font = TTF_OpenFont("assets/fonts/lazy.ttf", 28);
107 if (font == nullptr) {
108 cout << "Failed to load lazy font! SDL_ttf Error: " << TTF_GetError() << endl;
109 return RTWO_ERROR;
110 }
111
112 SDL_Surface* fontSDLSurface = TTF_RenderText_Solid(font, "Great success!", { 255, 255, 255 });
113 if (fontSDLSurface == nullptr) {
114 cout << "Unable to render text surface! SDL_ttf Error: " << TTF_GetError() << endl;
115 return RTWO_ERROR;
116 }
117
118 fontSDLTexture = SDL_CreateTextureFromSurface(renderer, fontSDLSurface);
119 if (fontSDLTexture == nullptr) {
120 cout << "Unable to create texture from rendered text! SDL Error: " << SDL_GetError() << endl;
121 SDL_FreeSurface(fontSDLSurface);
122 return RTWO_ERROR;
123 }
124
125 SDL_FreeSurface(fontSDLSurface);
126
127 // TODO: Load a PNG instead
128 SDL_Surface* imageSDLSurface = SDL_LoadBMP("assets/images/spaceship.bmp");
129 if (imageSDLSurface == nullptr) {
130 cout << "Unable to load image " << "spaceship.bmp" << "! SDL Error: " << SDL_GetError() << endl;
131 return RTWO_ERROR;
132 }
133
134 imageSDLTexture = SDL_CreateTextureFromSurface(renderer, imageSDLSurface);
135 if (imageSDLTexture == nullptr) {
136 cout << "Unable to create texture from BMP surface! SDL Error: " << SDL_GetError() << endl;
137 SDL_FreeSurface(imageSDLSurface);
138 return RTWO_ERROR;
139 }
140
141 SDL_FreeSurface(imageSDLSurface);
142
143 // In SDL 2.0.10 (currently, the latest), SDL_TEXTUREACCESS_TARGET is required to get a transparent overlay working
144 // However, the latest SDL version available through homebrew on Mac is 2.0.9, which requires SDL_TEXTUREACCESS_STREAMING
145 // I tried building sdl 2.0.10 (and sdl_image and sdl_ttf) from source on Mac, but had some issues, so this is easier
146 // until the homebrew recipe is updated
147 if (sdlVersion.major == 2 && sdlVersion.minor == 0 && sdlVersion.patch == 9) {
148 uiOverlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING,
149 gui->getWindowWidth(), gui->getWindowHeight());
150 } else {
151 uiOverlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
152 gui->getWindowWidth(), gui->getWindowHeight());
153 }
154
155 if (uiOverlay == nullptr) {
156 cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << endl;
157 return RTWO_ERROR;
158 }
159 if (SDL_SetTextureBlendMode(uiOverlay, SDL_BLENDMODE_BLEND) != 0) {
160 cout << "Unable to set texture blend mode! SDL Error: " << SDL_GetError() << endl;
161 return RTWO_ERROR;
162 }
163
164 SDL_SetRenderTarget(renderer, uiOverlay);
165
166 return RTWO_SUCCESS;
167}
168
169void VulkanGame::initVulkan() {
170 const vector<const char*> validationLayers = {
171 "VK_LAYER_KHRONOS_validation"
172 };
173 const vector<const char*> deviceExtensions = {
174 VK_KHR_SWAPCHAIN_EXTENSION_NAME
175 };
176
177 createVulkanInstance(validationLayers);
178 setupDebugMessenger();
179 createVulkanSurface();
180 pickPhysicalDevice(deviceExtensions);
181 createLogicalDevice(validationLayers, deviceExtensions);
182 createSwapChain();
183 createImageViews();
184 createRenderPass();
185 createCommandPool();
186
187 createImageResources();
188 createFramebuffers();
189
190 initMatrices();
191
192 // TODO: Figure out how much of ubo creation and associated variables should be in the pipeline class
193 // Maybe combine the ubo-related objects into a new class
194
195 initGraphicsPipelines();
196
197 overlayPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&OverlayVertex::pos));
198 overlayPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&OverlayVertex::texCoord));
199
200 overlayPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
201 VK_SHADER_STAGE_FRAGMENT_BIT, &sdlOverlayImageDescriptor);
202
203 addObject(overlayObjects, overlayPipeline,
204 {
205 {{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f}},
206 {{ 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
207 {{ 1.0f, -1.0f, 0.0f}, {1.0f, 0.0f}},
208 {{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f}}
209 }, {
210 0, 1, 2, 2, 3, 0
211 }, {}, false);
212
213 overlayPipeline.createDescriptorSetLayout();
214 overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
215 overlayPipeline.createDescriptorPool(swapChainImages);
216 overlayPipeline.createDescriptorSets(swapChainImages);
217
218 modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::pos));
219 modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::color));
220 modelPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&ModelVertex::texCoord));
221 modelPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ModelVertex::objIndex));
222
223 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
224 uniformBuffers_modelPipeline, uniformBuffersMemory_modelPipeline, uniformBufferInfoList_modelPipeline);
225
226 modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
227 VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_modelPipeline);
228 modelPipeline.addStorageDescriptor();
229
230 modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
231 VK_SHADER_STAGE_FRAGMENT_BIT, &floorTextureImageDescriptor);
232
233 addObject(modelObjects, modelPipeline,
234 addObjectIndex<ModelVertex>(modelObjects.size(), {
235 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
236 {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
237 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
238 {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
239 }), {
240 0, 1, 2, 2, 3, 0
241 }, {
242 mat4(1.0f)
243 }, false);
244
245 modelObjects.back().model_base =
246 translate(mat4(1.0f), vec3(0.0f, 0.0f, -2.0f));
247
248 updateObject(modelObjects, modelPipeline, modelObjects.size() - 1);
249
250 addObject(modelObjects, modelPipeline,
251 addObjectIndex<ModelVertex>(modelObjects.size(), {
252 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
253 {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
254 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
255 {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
256 }), {
257 0, 1, 2, 2, 3, 0
258 }, {
259 mat4(1.0f)
260 }, false);
261
262 modelObjects.back().model_base =
263 translate(mat4(1.0f), vec3(0.0f, 0.0f, -1.5f));
264
265 updateObject(modelObjects, modelPipeline, modelObjects.size() - 1);
266
267 modelPipeline.createDescriptorSetLayout();
268 modelPipeline.createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
269 modelPipeline.createDescriptorPool(swapChainImages);
270 modelPipeline.createDescriptorSets(swapChainImages);
271
272 shipPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ShipVertex::pos));
273 shipPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ShipVertex::color));
274 shipPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ShipVertex::normal));
275 shipPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&ShipVertex::objIndex));
276
277 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
278 uniformBuffers_shipPipeline, uniformBuffersMemory_shipPipeline, uniformBufferInfoList_shipPipeline);
279
280 shipPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
281 VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_shipPipeline);
282 shipPipeline.addStorageDescriptor();
283
284 // TODO: With the normals, indexing basically becomes pointless since no vertices will have exactly
285 // the same data. Add an option to make some pipelines not use indexing
286 addObject(shipObjects, shipPipeline,
287 addObjectIndex<ShipVertex>(shipObjects.size(),
288 addVertexNormals<ShipVertex>({
289
290 //back
291 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
292 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
293 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
294 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
295 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
296 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
297
298 // left back
299 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
300 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
301 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
302 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
303 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
304 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
305
306 // right back
307 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
308 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
309 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
310 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
311 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
312 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
313
314 // left mid
315 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
316 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
317 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
318 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
319 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
320 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
321
322 // right mid
323 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
324 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 0.3f}},
325 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
326 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 0.3f}},
327 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
328 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
329
330 // left front
331 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 1.0f}},
332 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
333 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
334
335 // right front
336 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
337 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
338 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 1.0f}},
339
340 // top back
341 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
342 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 1.0f}},
343 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 1.0f}},
344 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
345 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 1.0f}},
346 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
347
348 // bottom back
349 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
350 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
351 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
352 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
353 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
354 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
355
356 // top mid
357 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
358 {{ -0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
359 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
360 {{ -0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
361 {{ 0.5f, 0.3f, -2.0f}, {0.0f, 0.0f, 1.0f}},
362 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 1.0f}},
363
364 // bottom mid
365 {{ -0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
366 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
367 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
368 {{ 0.5f, 0.0f, -2.0f}, {0.0f, 0.0f, 1.0f}},
369 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
370 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 1.0f}},
371
372 // top front
373 {{-0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
374 {{ 0.25f, 0.3f, -3.0f}, {0.0f, 0.0f, 0.3f}},
375 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 0.3f}},
376
377 // bottom front
378 {{ 0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
379 {{-0.25f, 0.0f, -3.0f}, {0.0f, 0.0f, 0.3f}},
380 {{ 0.0f, 0.0f, -3.5f}, {0.0f, 0.0f, 0.3f}},
381
382 // left wing start back
383 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
384 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
385 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
386 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
387 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
388 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
389
390 // left wing start top
391 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
392 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
393 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
394 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
395 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
396 {{ -0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
397
398 // left wing start front
399 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
400 {{ -0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
401 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
402 {{ -0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
403 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
404 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
405
406 // left wing start bottom
407 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
408 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
409 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
410 {{ -0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
411 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
412 {{ -0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
413
414 // left wing end outside
415 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
416 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
417 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
418
419 // left wing end top
420 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
421 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
422 {{ -1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
423
424 // left wing end front
425 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
426 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
427 {{ -1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
428
429 // left wing end bottom
430 {{ -1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
431 {{ -2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
432 {{ -1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
433
434 // right wing start back
435 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
436 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
437 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
438 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
439 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
440 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
441
442 // right wing start top
443 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
444 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
445 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
446 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
447 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
448 {{ 0.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
449
450 // right wing start front
451 {{ 0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
452 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
453 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
454 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
455 {{ 0.5f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
456 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
457
458 // right wing start bottom
459 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
460 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
461 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
462 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
463 {{ 0.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
464 {{ 0.5f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
465
466 // right wing end outside
467 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
468 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
469 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
470
471 // right wing end top
472 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
473 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
474 {{ 1.5f, 0.3f, 0.0f}, {0.0f, 0.0f, 0.3f}},
475
476 // right wing end front
477 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
478 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
479 {{ 1.3f, 0.3f, -0.3f}, {0.0f, 0.0f, 0.3f}},
480
481 // right wing end bottom
482 {{ 2.2f, 0.15f, -0.8f}, {0.0f, 0.0f, 0.3f}},
483 {{ 1.5f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.3f}},
484 {{ 1.3f, 0.0f, -0.3f}, {0.0f, 0.0f, 0.3f}},
485 })), {
486 0, 1, 2, 3, 4, 5,
487 6, 7, 8, 9, 10, 11,
488 12, 13, 14, 15, 16, 17,
489 18, 19, 20, 21, 22, 23,
490 24, 25, 26, 27, 28, 29,
491 30, 31, 32,
492 33, 34, 35,
493 36, 37, 38, 39, 40, 41,
494 42, 43, 44, 45, 46, 47,
495 48, 49, 50, 51, 52, 53,
496 54, 55, 56, 57, 58, 59,
497 60, 61, 62,
498 63, 64, 65,
499 66, 67, 68, 69, 70, 71,
500 72, 73, 74, 75, 76, 77,
501 78, 79, 80, 81, 82, 83,
502 84, 85, 86, 87, 88, 89,
503 90, 91, 92,
504 93, 94, 95,
505 96, 97, 98,
506 99, 100, 101,
507 102, 103, 104, 105, 106, 107,
508 108, 109, 110, 111, 112, 113,
509 114, 115, 116, 117, 118, 119,
510 120, 121, 122, 123, 124, 125,
511 126, 127, 128,
512 129, 130, 131,
513 132, 133, 134,
514 135, 136, 137,
515 }, {
516 mat4(1.0f)
517 }, false);
518
519 shipPipeline.createDescriptorSetLayout();
520 shipPipeline.createPipeline("shaders/ship-vert.spv", "shaders/ship-frag.spv");
521 shipPipeline.createDescriptorPool(swapChainImages);
522 shipPipeline.createDescriptorSets(swapChainImages);
523
524 asteroidPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&AsteroidVertex::pos));
525 asteroidPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&AsteroidVertex::color));
526 asteroidPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&AsteroidVertex::normal));
527 asteroidPipeline.addAttribute(VK_FORMAT_R32_UINT, offset_of(&AsteroidVertex::objIndex));
528
529 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
530 uniformBuffers_asteroidPipeline, uniformBuffersMemory_asteroidPipeline, uniformBufferInfoList_asteroidPipeline);
531
532 asteroidPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
533 VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList_asteroidPipeline);
534 asteroidPipeline.addStorageDescriptor();
535
536 asteroidPipeline.createDescriptorSetLayout();
537 asteroidPipeline.createPipeline("shaders/asteroid-vert.spv", "shaders/asteroid-frag.spv");
538 asteroidPipeline.createDescriptorPool(swapChainImages);
539 asteroidPipeline.createDescriptorSets(swapChainImages);
540
541 cout << "Created all the graphics pipelines" << endl;
542
543 createCommandBuffers();
544
545 createSyncObjects();
546
547 shipObjects[0].model_base =
548 translate(mat4(1.0f), vec3(0.0f, -1.2f, 1.65f)) *
549 scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
550
551 updateObject(shipObjects, shipPipeline, 0);
552}
553
554void VulkanGame::initGraphicsPipelines() {
555 overlayPipeline = GraphicsPipeline_Vulkan<OverlayVertex, void*>(physicalDevice, device, renderPass,
556 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 4, 6, 0);
557
558 modelPipeline = GraphicsPipeline_Vulkan<ModelVertex, SSBO_ModelObject>(physicalDevice, device, renderPass,
559 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 16, 24, 10);
560
561 shipPipeline = GraphicsPipeline_Vulkan<ShipVertex, SSBO_ModelObject>(physicalDevice, device, renderPass,
562 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 138, 138, 10);
563
564 asteroidPipeline = GraphicsPipeline_Vulkan<AsteroidVertex, SSBO_Asteroid>(physicalDevice, device, renderPass,
565 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, swapChainImages, 24, 36, 10);
566}
567
568// TODO: Maybe changes the name to initScene() or something similar
569void VulkanGame::initMatrices() {
570 cam_pos = vec3(0.0f, 0.0f, 2.0f);
571
572 float cam_yaw = 0.0f;
573 float cam_pitch = -50.0f;
574
575 mat4 yaw_mat = rotate(mat4(1.0f), radians(-cam_yaw), vec3(0.0f, 1.0f, 0.0f));
576 mat4 pitch_mat = rotate(mat4(1.0f), radians(-cam_pitch), vec3(1.0f, 0.0f, 0.0f));
577
578 mat4 R_view = pitch_mat * yaw_mat;
579 mat4 T_view = translate(mat4(1.0f), vec3(-cam_pos.x, -cam_pos.y, -cam_pos.z));
580 viewMat = R_view * T_view;
581
582 projMat = perspective(radians(FOV_ANGLE), (float)swapChainExtent.width / (float)swapChainExtent.height, NEAR_CLIP, FAR_CLIP);
583 projMat[1][1] *= -1; // flip the y-axis so that +y is up
584
585 object_VP_mats.view = viewMat;
586 object_VP_mats.proj = projMat;
587
588 ship_VP_mats.view = viewMat;
589 ship_VP_mats.proj = projMat;
590
591 asteroid_VP_mats.view = viewMat;
592 asteroid_VP_mats.proj = projMat;
593}
594
595void VulkanGame::mainLoop() {
596 UIEvent e;
597 bool quit = false;
598
599 this->startTime = high_resolution_clock::now();
600 this->curTime = duration<float, seconds::period>(high_resolution_clock::now() - this->startTime).count();
601
602 lastSpawn_asteroid = this->curTime;
603
604 while (!quit) {
605
606 this->prevTime = this->curTime;
607 this->curTime = duration<float, seconds::period>(high_resolution_clock::now() - this->startTime).count();
608 this->elapsedTime = this->curTime - this->prevTime;
609
610 gui->processEvents();
611
612 while (gui->pollEvent(&e)) {
613 switch(e.type) {
614 case UI_EVENT_QUIT:
615 cout << "Quit event detected" << endl;
616 quit = true;
617 break;
618 case UI_EVENT_WINDOW:
619 cout << "Window event detected" << endl;
620 // Currently unused
621 break;
622 case UI_EVENT_WINDOWRESIZE:
623 cout << "Window resize event detected" << endl;
624 framebufferResized = true;
625 break;
626 case UI_EVENT_KEYDOWN:
627 if (e.key.keycode == SDL_SCANCODE_ESCAPE) {
628 quit = true;
629 } else if (e.key.keycode == SDL_SCANCODE_SPACE) {
630 cout << "Adding a plane" << endl;
631 float zOffset = -2.0f + (0.5f * modelObjects.size());
632
633 addObject(modelObjects, modelPipeline,
634 addObjectIndex<ModelVertex>(modelObjects.size(), {
635 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
636 {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
637 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
638 {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
639 }), {
640 0, 1, 2, 2, 3, 0
641 }, {
642 mat4(1.0f)
643 }, true);
644
645 modelObjects.back().model_base =
646 translate(mat4(1.0f), vec3(0.0f, 0.0f, zOffset));
647
648 updateObject(modelObjects, modelPipeline, modelObjects.size() - 1);
649 } else {
650 cout << "Key event detected" << endl;
651 }
652 break;
653 case UI_EVENT_KEYUP:
654 break;
655 case UI_EVENT_MOUSEBUTTONDOWN:
656 cout << "Mouse button down event detected" << endl;
657 break;
658 case UI_EVENT_MOUSEBUTTONUP:
659 cout << "Mouse button up event detected" << endl;
660 break;
661 case UI_EVENT_MOUSEMOTION:
662 break;
663 case UI_EVENT_UNKNOWN:
664 cout << "Unknown event type: 0x" << hex << e.unknown.eventType << dec << endl;
665 break;
666 default:
667 cout << "Unhandled UI event: " << e.type << endl;
668 }
669 }
670
671 // Check which keys are held down
672
673 if (gui->keyPressed(SDL_SCANCODE_LEFT)) {
674 shipObjects[0].model_transform = translate(mat4(1.0f), vec3(-this->shipSpeed * this->elapsedTime, 0.0f, 0.0f))
675 * shipObjects[0].model_transform;
676
677 updateObject(shipObjects, shipPipeline, 0);
678 } else if (gui->keyPressed(SDL_SCANCODE_RIGHT)) {
679 shipObjects[0].model_transform = translate(mat4(1.0f), vec3(this->shipSpeed * this->elapsedTime, 0.0f, 0.0f))
680 * shipObjects[0].model_transform;
681
682 updateObject(shipObjects, shipPipeline, 0);
683 }
684
685 // TODO: Remove this block of code and correctly update the center of all objects when they are transformed
686 if (gui->keyPressed(SDL_SCANCODE_X)) {
687 if (asteroidObjects.size() > 0 && !asteroidObjects[0].ssbo.deleted) {
688 asteroidObjects[0].model_transform = translate(mat4(1.0f), vec3(0.0f, 0.0f, this->asteroidSpeed * this->elapsedTime))
689 * asteroidObjects[0].model_transform;
690
691 vec3 obj_center = vec3(asteroid_VP_mats.view * vec4(asteroidObjects[0].center, 1.0f));
692
693 float closest = obj_center.z - asteroidObjects[0].radius;
694 cout << closest << " ? " << -NEAR_CLIP << endl;
695
696 updateObject(asteroidObjects, asteroidPipeline, 0);
697 }
698 }
699
700 renderUI();
701 renderScene();
702 }
703
704 vkDeviceWaitIdle(device);
705}
706
707// TODO: The only updates that need to happen once per Vulkan image are the SSBO ones,
708// which are already handled by updateObject(). Move this code to a different place,
709// where it will run just once per frame
710void VulkanGame::updateScene(uint32_t currentImage) {
711 for (int i = 0; i < modelObjects.size(); i++) {
712 modelObjects[i].model_transform =
713 translate(mat4(1.0f), vec3(0.0f, -2.0f, -0.0f)) *
714 rotate(mat4(1.0f), this->curTime * radians(90.0f), vec3(0.0f, 0.0f, 1.0f));
715
716 updateObject(modelObjects, modelPipeline, i);
717 }
718
719 for (int i = 0; i < asteroidObjects.size(); i++) {
720 if (!asteroidObjects[i].ssbo.deleted) {
721 asteroidObjects[i].model_transform =
722 translate(mat4(1.0f), vec3(0.0f, 0.0f, this->asteroidSpeed * this->elapsedTime)) *
723 asteroidObjects[i].model_transform;
724
725 updateObject(asteroidObjects, asteroidPipeline, i);
726 }
727 }
728
729 if (this->curTime - this->lastSpawn_asteroid > this->spawnRate_asteroid) {
730 this->lastSpawn_asteroid = this->curTime;
731
732 addObject(asteroidObjects, asteroidPipeline,
733 addObjectIndex<AsteroidVertex>(asteroidObjects.size(),
734 addVertexNormals<AsteroidVertex>({
735
736 // front
737 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
738 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
739 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
740 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
741 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
742 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
743
744 // top
745 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
746 {{-1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
747 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
748 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
749 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
750 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
751
752 // bottom
753 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
754 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
755 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
756 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
757 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
758 {{ 1.0f, -1.0f, -1.0}, {0.4f, 0.4f, 0.4f}},
759
760 // back
761 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
762 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
763 {{-1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
764 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
765 {{ 1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
766 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
767
768 // right
769 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
770 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
771 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
772 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
773 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
774 {{ 1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
775
776 // left
777 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
778 {{-1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
779 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
780 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
781 {{-1.0f, -1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
782 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
783 })), {
784 0, 1, 2, 3, 4, 5,
785 6, 7, 8, 9, 10, 11,
786 12, 13, 14, 15, 16, 17,
787 18, 19, 20, 21, 22, 23,
788 24, 25, 26, 27, 28, 29,
789 30, 31, 32, 33, 34, 35,
790 }, {
791 mat4(1.0f),
792 10.0f,
793 0
794 }, true);
795
796 // translate(mat4(1.0f), vec3(getRandomNum(-1.3f, 1.3f), -1.2f, getRandomNum(-5.5f, -4.5f))) *
797 // translate(mat4(1.0f), vec3(0.0504826f, -1.2f, 1.0f)) *
798 asteroidObjects.back().model_base =
799 translate(mat4(1.0f), vec3(getRandomNum(-1.3f, 1.3f), -1.2f, -2.0f)) *
800 rotate(mat4(1.0f), radians(60.0f), vec3(1.0f, 1.0f, -1.0f)) *
801 scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
802
803 updateObject(asteroidObjects, asteroidPipeline, asteroidObjects.size() - 1);
804 }
805
806 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_modelPipeline[currentImage], 0, object_VP_mats);
807
808 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_shipPipeline[currentImage], 0, ship_VP_mats);
809
810 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_asteroidPipeline[currentImage], 0, asteroid_VP_mats);
811}
812
813void VulkanGame::renderUI() {
814 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
815 SDL_RenderClear(renderer);
816
817 SDL_Rect rect = {280, 220, 100, 100};
818 SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
819 SDL_RenderFillRect(renderer, &rect);
820
821 rect = {10, 10, 0, 0};
822 SDL_QueryTexture(fontSDLTexture, nullptr, nullptr, &(rect.w), &(rect.h));
823 SDL_RenderCopy(renderer, fontSDLTexture, nullptr, &rect);
824
825 rect = {10, 80, 0, 0};
826 SDL_QueryTexture(imageSDLTexture, nullptr, nullptr, &(rect.w), &(rect.h));
827 SDL_RenderCopy(renderer, imageSDLTexture, nullptr, &rect);
828
829 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0xFF, 0xFF);
830 SDL_RenderDrawLine(renderer, 50, 5, 150, 500);
831
832 VulkanUtils::populateVulkanImageFromSDLTexture(device, physicalDevice, commandPool, uiOverlay, renderer,
833 sdlOverlayImage, graphicsQueue);
834}
835
836void VulkanGame::renderScene() {
837 vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, numeric_limits<uint64_t>::max());
838
839 uint32_t imageIndex;
840
841 VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(),
842 imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
843
844 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
845 recreateSwapChain();
846 return;
847 } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
848 throw runtime_error("failed to acquire swap chain image!");
849 }
850
851 // TODO: Figure out a more elegant way to only do updates and render the UI once per scene render
852 // Probably move some of the renderScene() code into a higher function that updates the UI, and renders
853 // the UI and scene
854 updateScene(imageIndex);
855
856 VkSubmitInfo submitInfo = {};
857 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
858
859 VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
860 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
861
862 submitInfo.waitSemaphoreCount = 1;
863 submitInfo.pWaitSemaphores = waitSemaphores;
864 submitInfo.pWaitDstStageMask = waitStages;
865 submitInfo.commandBufferCount = 1;
866 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
867
868 VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
869
870 submitInfo.signalSemaphoreCount = 1;
871 submitInfo.pSignalSemaphores = signalSemaphores;
872
873 vkResetFences(device, 1, &inFlightFences[currentFrame]);
874
875 if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
876 throw runtime_error("failed to submit draw command buffer!");
877 }
878
879 VkPresentInfoKHR presentInfo = {};
880 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
881 presentInfo.waitSemaphoreCount = 1;
882 presentInfo.pWaitSemaphores = signalSemaphores;
883
884 VkSwapchainKHR swapChains[] = { swapChain };
885 presentInfo.swapchainCount = 1;
886 presentInfo.pSwapchains = swapChains;
887 presentInfo.pImageIndices = &imageIndex;
888 presentInfo.pResults = nullptr;
889
890 result = vkQueuePresentKHR(presentQueue, &presentInfo);
891
892 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
893 framebufferResized = false;
894 recreateSwapChain();
895 } else if (result != VK_SUCCESS) {
896 throw runtime_error("failed to present swap chain image!");
897 }
898
899 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
900 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
901}
902
903void VulkanGame::cleanup() {
904 cleanupSwapChain();
905
906 VulkanUtils::destroyVulkanImage(device, floorTextureImage);
907 VulkanUtils::destroyVulkanImage(device, sdlOverlayImage);
908
909 vkDestroySampler(device, textureSampler, nullptr);
910
911 modelPipeline.cleanupBuffers();
912 overlayPipeline.cleanupBuffers();
913 shipPipeline.cleanupBuffers();
914 asteroidPipeline.cleanupBuffers();
915
916 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
917 vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
918 vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
919 vkDestroyFence(device, inFlightFences[i], nullptr);
920 }
921
922 vkDestroyCommandPool(device, commandPool, nullptr);
923 vkDestroyDevice(device, nullptr);
924 vkDestroySurfaceKHR(instance, surface, nullptr);
925
926 if (ENABLE_VALIDATION_LAYERS) {
927 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
928 }
929
930 vkDestroyInstance(instance, nullptr);
931
932 // TODO: Check if any of these functions accept null parameters
933 // If they do, I don't need to check for that
934
935 if (uiOverlay != nullptr) {
936 SDL_DestroyTexture(uiOverlay);
937 uiOverlay = nullptr;
938 }
939
940 if (fontSDLTexture != nullptr) {
941 SDL_DestroyTexture(fontSDLTexture);
942 fontSDLTexture = nullptr;
943 }
944
945 if (imageSDLTexture != nullptr) {
946 SDL_DestroyTexture(imageSDLTexture);
947 imageSDLTexture = nullptr;
948 }
949
950 TTF_CloseFont(font);
951 font = nullptr;
952
953 SDL_DestroyRenderer(renderer);
954 renderer = nullptr;
955
956 gui->destroyWindow();
957 gui->shutdown();
958 delete gui;
959}
960
961void VulkanGame::createVulkanInstance(const vector<const char*> &validationLayers) {
962 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
963 throw runtime_error("validation layers requested, but not available!");
964 }
965
966 VkApplicationInfo appInfo = {};
967 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
968 appInfo.pApplicationName = "Vulkan Game";
969 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
970 appInfo.pEngineName = "No Engine";
971 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
972 appInfo.apiVersion = VK_API_VERSION_1_0;
973
974 VkInstanceCreateInfo createInfo = {};
975 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
976 createInfo.pApplicationInfo = &appInfo;
977
978 vector<const char*> extensions = gui->getRequiredExtensions();
979 if (ENABLE_VALIDATION_LAYERS) {
980 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
981 }
982
983 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
984 createInfo.ppEnabledExtensionNames = extensions.data();
985
986 cout << endl << "Extensions:" << endl;
987 for (const char* extensionName : extensions) {
988 cout << extensionName << endl;
989 }
990 cout << endl;
991
992 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
993 if (ENABLE_VALIDATION_LAYERS) {
994 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
995 createInfo.ppEnabledLayerNames = validationLayers.data();
996
997 populateDebugMessengerCreateInfo(debugCreateInfo);
998 createInfo.pNext = &debugCreateInfo;
999 } else {
1000 createInfo.enabledLayerCount = 0;
1001
1002 createInfo.pNext = nullptr;
1003 }
1004
1005 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
1006 throw runtime_error("failed to create instance!");
1007 }
1008}
1009
1010void VulkanGame::setupDebugMessenger() {
1011 if (!ENABLE_VALIDATION_LAYERS) return;
1012
1013 VkDebugUtilsMessengerCreateInfoEXT createInfo;
1014 populateDebugMessengerCreateInfo(createInfo);
1015
1016 if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
1017 throw runtime_error("failed to set up debug messenger!");
1018 }
1019}
1020
1021void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
1022 createInfo = {};
1023 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
1024 createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
1025 createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
1026 createInfo.pfnUserCallback = debugCallback;
1027}
1028
1029VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
1030 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
1031 VkDebugUtilsMessageTypeFlagsEXT messageType,
1032 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
1033 void* pUserData) {
1034 cerr << "validation layer: " << pCallbackData->pMessage << endl;
1035
1036 return VK_FALSE;
1037}
1038
1039void VulkanGame::createVulkanSurface() {
1040 if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
1041 throw runtime_error("failed to create window surface!");
1042 }
1043}
1044
1045void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
1046 uint32_t deviceCount = 0;
1047 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
1048
1049 if (deviceCount == 0) {
1050 throw runtime_error("failed to find GPUs with Vulkan support!");
1051 }
1052
1053 vector<VkPhysicalDevice> devices(deviceCount);
1054 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
1055
1056 cout << endl << "Graphics cards:" << endl;
1057 for (const VkPhysicalDevice& device : devices) {
1058 if (isDeviceSuitable(device, deviceExtensions)) {
1059 physicalDevice = device;
1060 break;
1061 }
1062 }
1063 cout << endl;
1064
1065 if (physicalDevice == VK_NULL_HANDLE) {
1066 throw runtime_error("failed to find a suitable GPU!");
1067 }
1068}
1069
1070bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice,
1071 const vector<const char*>& deviceExtensions) {
1072 VkPhysicalDeviceProperties deviceProperties;
1073 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
1074
1075 cout << "Device: " << deviceProperties.deviceName << endl;
1076
1077 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1078 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
1079 bool swapChainAdequate = false;
1080
1081 if (extensionsSupported) {
1082 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
1083 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
1084 }
1085
1086 VkPhysicalDeviceFeatures supportedFeatures;
1087 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
1088
1089 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
1090}
1091
1092void VulkanGame::createLogicalDevice(
1093 const vector<const char*> validationLayers, const vector<const char*>& deviceExtensions) {
1094 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1095
1096 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
1097 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1098
1099 float queuePriority = 1.0f;
1100 for (uint32_t queueFamily : uniqueQueueFamilies) {
1101 VkDeviceQueueCreateInfo queueCreateInfo = {};
1102 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1103 queueCreateInfo.queueFamilyIndex = queueFamily;
1104 queueCreateInfo.queueCount = 1;
1105 queueCreateInfo.pQueuePriorities = &queuePriority;
1106
1107 queueCreateInfoList.push_back(queueCreateInfo);
1108 }
1109
1110 VkPhysicalDeviceFeatures deviceFeatures = {};
1111 deviceFeatures.samplerAnisotropy = VK_TRUE;
1112
1113 VkDeviceCreateInfo createInfo = {};
1114 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
1115 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
1116 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
1117
1118 createInfo.pEnabledFeatures = &deviceFeatures;
1119
1120 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
1121 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
1122
1123 // These fields are ignored by up-to-date Vulkan implementations,
1124 // but it's a good idea to set them for backwards compatibility
1125 if (ENABLE_VALIDATION_LAYERS) {
1126 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
1127 createInfo.ppEnabledLayerNames = validationLayers.data();
1128 } else {
1129 createInfo.enabledLayerCount = 0;
1130 }
1131
1132 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
1133 throw runtime_error("failed to create logical device!");
1134 }
1135
1136 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
1137 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
1138}
1139
1140void VulkanGame::createSwapChain() {
1141 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
1142
1143 VkSurfaceFormatKHR surfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(swapChainSupport.formats);
1144 VkPresentModeKHR presentMode = VulkanUtils::chooseSwapPresentMode(swapChainSupport.presentModes);
1145 VkExtent2D extent = VulkanUtils::chooseSwapExtent(swapChainSupport.capabilities, gui->getWindowWidth(), gui->getWindowHeight());
1146
1147 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
1148 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
1149 imageCount = swapChainSupport.capabilities.maxImageCount;
1150 }
1151
1152 VkSwapchainCreateInfoKHR createInfo = {};
1153 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1154 createInfo.surface = surface;
1155 createInfo.minImageCount = imageCount;
1156 createInfo.imageFormat = surfaceFormat.format;
1157 createInfo.imageColorSpace = surfaceFormat.colorSpace;
1158 createInfo.imageExtent = extent;
1159 createInfo.imageArrayLayers = 1;
1160 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1161
1162 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1163 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1164
1165 if (indices.graphicsFamily != indices.presentFamily) {
1166 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
1167 createInfo.queueFamilyIndexCount = 2;
1168 createInfo.pQueueFamilyIndices = queueFamilyIndices;
1169 } else {
1170 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1171 createInfo.queueFamilyIndexCount = 0;
1172 createInfo.pQueueFamilyIndices = nullptr;
1173 }
1174
1175 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
1176 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1177 createInfo.presentMode = presentMode;
1178 createInfo.clipped = VK_TRUE;
1179 createInfo.oldSwapchain = VK_NULL_HANDLE;
1180
1181 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
1182 throw runtime_error("failed to create swap chain!");
1183 }
1184
1185 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
1186 swapChainImages.resize(imageCount);
1187 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
1188
1189 swapChainImageFormat = surfaceFormat.format;
1190 swapChainExtent = extent;
1191}
1192
1193void VulkanGame::createImageViews() {
1194 swapChainImageViews.resize(swapChainImages.size());
1195
1196 for (size_t i = 0; i < swapChainImages.size(); i++) {
1197 swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainImageFormat,
1198 VK_IMAGE_ASPECT_COLOR_BIT);
1199 }
1200}
1201
1202void VulkanGame::createRenderPass() {
1203 VkAttachmentDescription colorAttachment = {};
1204 colorAttachment.format = swapChainImageFormat;
1205 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1206 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1207 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1208 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1209 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1210 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1211 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1212
1213 VkAttachmentReference colorAttachmentRef = {};
1214 colorAttachmentRef.attachment = 0;
1215 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1216
1217 VkAttachmentDescription depthAttachment = {};
1218 depthAttachment.format = findDepthFormat();
1219 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1220 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1221 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1222 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1223 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1224 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1225 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1226
1227 VkAttachmentReference depthAttachmentRef = {};
1228 depthAttachmentRef.attachment = 1;
1229 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1230
1231 VkSubpassDescription subpass = {};
1232 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1233 subpass.colorAttachmentCount = 1;
1234 subpass.pColorAttachments = &colorAttachmentRef;
1235 subpass.pDepthStencilAttachment = &depthAttachmentRef;
1236
1237 VkSubpassDependency dependency = {};
1238 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1239 dependency.dstSubpass = 0;
1240 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1241 dependency.srcAccessMask = 0;
1242 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1243 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1244
1245 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
1246 VkRenderPassCreateInfo renderPassInfo = {};
1247 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1248 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1249 renderPassInfo.pAttachments = attachments.data();
1250 renderPassInfo.subpassCount = 1;
1251 renderPassInfo.pSubpasses = &subpass;
1252 renderPassInfo.dependencyCount = 1;
1253 renderPassInfo.pDependencies = &dependency;
1254
1255 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
1256 throw runtime_error("failed to create render pass!");
1257 }
1258}
1259
1260VkFormat VulkanGame::findDepthFormat() {
1261 return VulkanUtils::findSupportedFormat(
1262 physicalDevice,
1263 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
1264 VK_IMAGE_TILING_OPTIMAL,
1265 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1266 );
1267}
1268
1269void VulkanGame::createCommandPool() {
1270 QueueFamilyIndices queueFamilyIndices = VulkanUtils::findQueueFamilies(physicalDevice, surface);;
1271
1272 VkCommandPoolCreateInfo poolInfo = {};
1273 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1274 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
1275 poolInfo.flags = 0;
1276
1277 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
1278 throw runtime_error("failed to create graphics command pool!");
1279 }
1280}
1281
1282void VulkanGame::createImageResources() {
1283 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
1284 depthImage, graphicsQueue);
1285
1286 createTextureSampler();
1287
1288 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, commandPool, "textures/texture.jpg",
1289 floorTextureImage, graphicsQueue);
1290
1291 floorTextureImageDescriptor = {};
1292 floorTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1293 floorTextureImageDescriptor.imageView = floorTextureImage.imageView;
1294 floorTextureImageDescriptor.sampler = textureSampler;
1295
1296 VulkanUtils::createVulkanImageFromSDLTexture(device, physicalDevice, uiOverlay, sdlOverlayImage);
1297
1298 sdlOverlayImageDescriptor = {};
1299 sdlOverlayImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1300 sdlOverlayImageDescriptor.imageView = sdlOverlayImage.imageView;
1301 sdlOverlayImageDescriptor.sampler = textureSampler;
1302}
1303
1304void VulkanGame::createTextureSampler() {
1305 VkSamplerCreateInfo samplerInfo = {};
1306 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1307 samplerInfo.magFilter = VK_FILTER_LINEAR;
1308 samplerInfo.minFilter = VK_FILTER_LINEAR;
1309
1310 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1311 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1312 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1313
1314 samplerInfo.anisotropyEnable = VK_TRUE;
1315 samplerInfo.maxAnisotropy = 16;
1316 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
1317 samplerInfo.unnormalizedCoordinates = VK_FALSE;
1318 samplerInfo.compareEnable = VK_FALSE;
1319 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
1320 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
1321 samplerInfo.mipLodBias = 0.0f;
1322 samplerInfo.minLod = 0.0f;
1323 samplerInfo.maxLod = 0.0f;
1324
1325 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
1326 throw runtime_error("failed to create texture sampler!");
1327 }
1328}
1329
1330void VulkanGame::createFramebuffers() {
1331 swapChainFramebuffers.resize(swapChainImageViews.size());
1332
1333 for (size_t i = 0; i < swapChainImageViews.size(); i++) {
1334 array<VkImageView, 2> attachments = {
1335 swapChainImageViews[i],
1336 depthImage.imageView
1337 };
1338
1339 VkFramebufferCreateInfo framebufferInfo = {};
1340 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1341 framebufferInfo.renderPass = renderPass;
1342 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1343 framebufferInfo.pAttachments = attachments.data();
1344 framebufferInfo.width = swapChainExtent.width;
1345 framebufferInfo.height = swapChainExtent.height;
1346 framebufferInfo.layers = 1;
1347
1348 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
1349 throw runtime_error("failed to create framebuffer!");
1350 }
1351 }
1352}
1353
1354void VulkanGame::createCommandBuffers() {
1355 commandBuffers.resize(swapChainImages.size());
1356
1357 VkCommandBufferAllocateInfo allocInfo = {};
1358 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1359 allocInfo.commandPool = commandPool;
1360 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1361 allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
1362
1363 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
1364 throw runtime_error("failed to allocate command buffers!");
1365 }
1366
1367 for (size_t i = 0; i < commandBuffers.size(); i++) {
1368 VkCommandBufferBeginInfo beginInfo = {};
1369 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1370 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
1371 beginInfo.pInheritanceInfo = nullptr;
1372
1373 if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
1374 throw runtime_error("failed to begin recording command buffer!");
1375 }
1376
1377 VkRenderPassBeginInfo renderPassInfo = {};
1378 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1379 renderPassInfo.renderPass = renderPass;
1380 renderPassInfo.framebuffer = swapChainFramebuffers[i];
1381 renderPassInfo.renderArea.offset = { 0, 0 };
1382 renderPassInfo.renderArea.extent = swapChainExtent;
1383
1384 array<VkClearValue, 2> clearValues = {};
1385 clearValues[0].color = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
1386 clearValues[1].depthStencil = { 1.0f, 0 };
1387
1388 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
1389 renderPassInfo.pClearValues = clearValues.data();
1390
1391 vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1392
1393 modelPipeline.createRenderCommands(commandBuffers[i], i);
1394 shipPipeline.createRenderCommands(commandBuffers[i], i);
1395 asteroidPipeline.createRenderCommands(commandBuffers[i], i);
1396
1397 // Always render this pipeline last
1398 overlayPipeline.createRenderCommands(commandBuffers[i], i);
1399
1400 vkCmdEndRenderPass(commandBuffers[i]);
1401
1402 if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
1403 throw runtime_error("failed to record command buffer!");
1404 }
1405 }
1406}
1407
1408void VulkanGame::createSyncObjects() {
1409 imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1410 renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1411 inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
1412
1413 VkSemaphoreCreateInfo semaphoreInfo = {};
1414 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1415
1416 VkFenceCreateInfo fenceInfo = {};
1417 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1418 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1419
1420 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1421 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
1422 vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
1423 vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
1424 throw runtime_error("failed to create synchronization objects for a frame!");
1425 }
1426 }
1427}
1428
1429void VulkanGame::createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags,
1430 vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory, vector<VkDescriptorBufferInfo>& bufferInfoList) {
1431 buffers.resize(swapChainImages.size());
1432 buffersMemory.resize(swapChainImages.size());
1433 bufferInfoList.resize(swapChainImages.size());
1434
1435 for (size_t i = 0; i < swapChainImages.size(); i++) {
1436 VulkanUtils::createBuffer(device, physicalDevice, bufferSize, flags,
1437 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1438 buffers[i], buffersMemory[i]);
1439
1440 bufferInfoList[i].buffer = buffers[i];
1441 bufferInfoList[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
1442 bufferInfoList[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
1443 }
1444}
1445
1446// TODO: Fix the crash that happens when alt-tabbing
1447void VulkanGame::recreateSwapChain() {
1448 cout << "Recreating swap chain" << endl;
1449 gui->refreshWindowSize();
1450
1451 while (gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0 ||
1452 (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0) {
1453 SDL_WaitEvent(nullptr);
1454 gui->refreshWindowSize();
1455 }
1456
1457 vkDeviceWaitIdle(device);
1458
1459 cleanupSwapChain();
1460
1461 createSwapChain();
1462 createImageViews();
1463 createRenderPass();
1464
1465 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
1466 depthImage, graphicsQueue);
1467 createFramebuffers();
1468
1469 // TODO: Move UBO creation/management into GraphicsPipeline_Vulkan, like I did with SSBOs
1470
1471 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1472 uniformBuffers_modelPipeline, uniformBuffersMemory_modelPipeline, uniformBufferInfoList_modelPipeline);
1473
1474 modelPipeline.updateRenderPass(renderPass);
1475 modelPipeline.createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
1476 modelPipeline.createDescriptorPool(swapChainImages);
1477 modelPipeline.createDescriptorSets(swapChainImages);
1478
1479 overlayPipeline.updateRenderPass(renderPass);
1480 overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
1481 overlayPipeline.createDescriptorPool(swapChainImages);
1482 overlayPipeline.createDescriptorSets(swapChainImages);
1483
1484 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1485 uniformBuffers_shipPipeline, uniformBuffersMemory_shipPipeline, uniformBufferInfoList_shipPipeline);
1486
1487 shipPipeline.updateRenderPass(renderPass);
1488 shipPipeline.createPipeline("shaders/ship-vert.spv", "shaders/ship-frag.spv");
1489 shipPipeline.createDescriptorPool(swapChainImages);
1490 shipPipeline.createDescriptorSets(swapChainImages);
1491
1492 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1493 uniformBuffers_asteroidPipeline, uniformBuffersMemory_asteroidPipeline, uniformBufferInfoList_asteroidPipeline);
1494
1495 asteroidPipeline.updateRenderPass(renderPass);
1496 asteroidPipeline.createPipeline("shaders/asteroid-vert.spv", "shaders/asteroid-frag.spv");
1497 asteroidPipeline.createDescriptorPool(swapChainImages);
1498 asteroidPipeline.createDescriptorSets(swapChainImages);
1499
1500 createCommandBuffers();
1501}
1502
1503void VulkanGame::cleanupSwapChain() {
1504 VulkanUtils::destroyVulkanImage(device, depthImage);
1505
1506 for (VkFramebuffer framebuffer : swapChainFramebuffers) {
1507 vkDestroyFramebuffer(device, framebuffer, nullptr);
1508 }
1509
1510 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
1511
1512 overlayPipeline.cleanup();
1513 modelPipeline.cleanup();
1514 shipPipeline.cleanup();
1515 asteroidPipeline.cleanup();
1516
1517 for (size_t i = 0; i < uniformBuffers_modelPipeline.size(); i++) {
1518 vkDestroyBuffer(device, uniformBuffers_modelPipeline[i], nullptr);
1519 vkFreeMemory(device, uniformBuffersMemory_modelPipeline[i], nullptr);
1520 }
1521
1522 for (size_t i = 0; i < uniformBuffers_shipPipeline.size(); i++) {
1523 vkDestroyBuffer(device, uniformBuffers_shipPipeline[i], nullptr);
1524 vkFreeMemory(device, uniformBuffersMemory_shipPipeline[i], nullptr);
1525 }
1526
1527 for (size_t i = 0; i < uniformBuffers_asteroidPipeline.size(); i++) {
1528 vkDestroyBuffer(device, uniformBuffers_asteroidPipeline[i], nullptr);
1529 vkFreeMemory(device, uniformBuffersMemory_asteroidPipeline[i], nullptr);
1530 }
1531
1532 vkDestroyRenderPass(device, renderPass, nullptr);
1533
1534 for (VkImageView imageView : swapChainImageViews) {
1535 vkDestroyImageView(device, imageView, nullptr);
1536 }
1537
1538 vkDestroySwapchainKHR(device, swapChain, nullptr);
1539}
Note: See TracBrowser for help on using the repository browser.