source: opengl-game/vulkan-game.cpp@ 5ba732a

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

Add a modified flag to SceneObject and, instead of copying an object's data to the graphics card immediately after it is updated, instead set the flag to true and update all modified objects at once right before rendering

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