source: opengl-game/vulkan-game.cpp@ 2ba5617

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

Center each object before, rather than after, it is copied to the GPU so that the centered vertices are copied, update an object's center when its model matrix is modified in updateObject(), mark an asteroid object as deleted when it moves offscreen, and randomize the initial z-coordinate for a new asteroid

  • Property mode set to 100644
File size: 60.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
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 renderUI();
686 renderScene();
687 }
688
689 vkDeviceWaitIdle(device);
690}
691
692// TODO: The only updates that need to happen once per Vulkan image are the SSBO ones,
693// which are already handled by updateObject(). Move this code to a different place,
694// where it will run just once per frame
695void VulkanGame::updateScene(uint32_t currentImage) {
696 for (int i = 0; i < modelObjects.size(); i++) {
697 modelObjects[i].model_transform =
698 translate(mat4(1.0f), vec3(0.0f, -2.0f, -0.0f)) *
699 rotate(mat4(1.0f), this->curTime * radians(90.0f), vec3(0.0f, 0.0f, 1.0f));
700
701 updateObject(modelObjects, modelPipeline, i);
702 }
703
704 for (int i = 0; i < this->asteroidObjects.size(); i++) {
705 if (!this->asteroidObjects[i].ssbo.deleted) {
706 vec3 objCenter = vec3(viewMat * vec4(this->asteroidObjects[i].center, 1.0f));
707
708 if ((objCenter.z - this->asteroidObjects[i].radius) > -NEAR_CLIP) {
709 this->asteroidObjects[i].ssbo.deleted = true;
710 } else {
711 this->asteroidObjects[i].model_transform =
712 translate(mat4(1.0f), vec3(0.0f, 0.0f, this->asteroidSpeed * this->elapsedTime)) *
713 this->asteroidObjects[i].model_transform;
714 }
715
716 updateObject(asteroidObjects, asteroidPipeline, i);
717 }
718 }
719
720 if (this->curTime - this->lastSpawn_asteroid > this->spawnRate_asteroid) {
721 this->lastSpawn_asteroid = this->curTime;
722
723 addObject(asteroidObjects, asteroidPipeline,
724 addObjectIndex<AsteroidVertex>(asteroidObjects.size(),
725 addVertexNormals<AsteroidVertex>({
726
727 // front
728 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
729 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
730 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
731 {{ 1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
732 {{-1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
733 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
734
735 // top
736 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
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
743 // bottom
744 {{ 1.0f, -1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
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.0}, {0.4f, 0.4f, 0.4f}},
750
751 // back
752 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
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
759 // right
760 {{ 1.0f, 1.0f, -1.0f}, {0.4f, 0.4f, 0.4f}},
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
767 // left
768 {{-1.0f, 1.0f, 1.0f}, {0.4f, 0.4f, 0.4f}},
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 })), {
775 0, 1, 2, 3, 4, 5,
776 6, 7, 8, 9, 10, 11,
777 12, 13, 14, 15, 16, 17,
778 18, 19, 20, 21, 22, 23,
779 24, 25, 26, 27, 28, 29,
780 30, 31, 32, 33, 34, 35,
781 }, {
782 mat4(1.0f),
783 10.0f,
784 0
785 }, true);
786
787 // This accounts for the scaling in model_base.
788 // Dividing by 8 instead of 10 since the bounding radius algorithm
789 // under-calculates the true value.
790 // TODO: Figure out the best way to take scaling into account when calculating the radius
791 // Keep in mind that the main complicating factor is the currently poor radius calculation
792 asteroidObjects.back().radius /= 8.0f;
793
794 asteroidObjects.back().model_base =
795 translate(mat4(1.0f), vec3(getRandomNum(-1.3f, 1.3f), -1.2f, getRandomNum(-5.5f, -4.5f))) *
796 rotate(mat4(1.0f), radians(60.0f), vec3(1.0f, 1.0f, -1.0f)) *
797 scale(mat4(1.0f), vec3(0.1f, 0.1f, 0.1f));
798
799 updateObject(asteroidObjects, asteroidPipeline, asteroidObjects.size() - 1);
800 }
801
802 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_modelPipeline[currentImage], 0, object_VP_mats);
803
804 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_shipPipeline[currentImage], 0, ship_VP_mats);
805
806 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory_asteroidPipeline[currentImage], 0, asteroid_VP_mats);
807}
808
809void VulkanGame::renderUI() {
810 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
811 SDL_RenderClear(renderer);
812
813 SDL_Rect rect = {280, 220, 100, 100};
814 SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
815 SDL_RenderFillRect(renderer, &rect);
816
817 rect = {10, 10, 0, 0};
818 SDL_QueryTexture(fontSDLTexture, nullptr, nullptr, &(rect.w), &(rect.h));
819 SDL_RenderCopy(renderer, fontSDLTexture, nullptr, &rect);
820
821 rect = {10, 80, 0, 0};
822 SDL_QueryTexture(imageSDLTexture, nullptr, nullptr, &(rect.w), &(rect.h));
823 SDL_RenderCopy(renderer, imageSDLTexture, nullptr, &rect);
824
825 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0xFF, 0xFF);
826 SDL_RenderDrawLine(renderer, 50, 5, 150, 500);
827
828 VulkanUtils::populateVulkanImageFromSDLTexture(device, physicalDevice, commandPool, uiOverlay, renderer,
829 sdlOverlayImage, graphicsQueue);
830}
831
832void VulkanGame::renderScene() {
833 vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, numeric_limits<uint64_t>::max());
834
835 uint32_t imageIndex;
836
837 VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(),
838 imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
839
840 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
841 recreateSwapChain();
842 return;
843 } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
844 throw runtime_error("failed to acquire swap chain image!");
845 }
846
847 // TODO: Figure out a more elegant way to only do updates and render the UI once per scene render
848 // Probably move some of the renderScene() code into a higher function that updates the UI, and renders
849 // the UI and scene
850 updateScene(imageIndex);
851
852 VkSubmitInfo submitInfo = {};
853 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
854
855 VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
856 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
857
858 submitInfo.waitSemaphoreCount = 1;
859 submitInfo.pWaitSemaphores = waitSemaphores;
860 submitInfo.pWaitDstStageMask = waitStages;
861 submitInfo.commandBufferCount = 1;
862 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
863
864 VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
865
866 submitInfo.signalSemaphoreCount = 1;
867 submitInfo.pSignalSemaphores = signalSemaphores;
868
869 vkResetFences(device, 1, &inFlightFences[currentFrame]);
870
871 if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
872 throw runtime_error("failed to submit draw command buffer!");
873 }
874
875 VkPresentInfoKHR presentInfo = {};
876 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
877 presentInfo.waitSemaphoreCount = 1;
878 presentInfo.pWaitSemaphores = signalSemaphores;
879
880 VkSwapchainKHR swapChains[] = { swapChain };
881 presentInfo.swapchainCount = 1;
882 presentInfo.pSwapchains = swapChains;
883 presentInfo.pImageIndices = &imageIndex;
884 presentInfo.pResults = nullptr;
885
886 result = vkQueuePresentKHR(presentQueue, &presentInfo);
887
888 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
889 framebufferResized = false;
890 recreateSwapChain();
891 } else if (result != VK_SUCCESS) {
892 throw runtime_error("failed to present swap chain image!");
893 }
894
895 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
896 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
897}
898
899void VulkanGame::cleanup() {
900 cleanupSwapChain();
901
902 VulkanUtils::destroyVulkanImage(device, floorTextureImage);
903 VulkanUtils::destroyVulkanImage(device, sdlOverlayImage);
904
905 vkDestroySampler(device, textureSampler, nullptr);
906
907 modelPipeline.cleanupBuffers();
908 overlayPipeline.cleanupBuffers();
909 shipPipeline.cleanupBuffers();
910 asteroidPipeline.cleanupBuffers();
911
912 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
913 vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
914 vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
915 vkDestroyFence(device, inFlightFences[i], nullptr);
916 }
917
918 vkDestroyCommandPool(device, commandPool, nullptr);
919 vkDestroyDevice(device, nullptr);
920 vkDestroySurfaceKHR(instance, surface, nullptr);
921
922 if (ENABLE_VALIDATION_LAYERS) {
923 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
924 }
925
926 vkDestroyInstance(instance, nullptr);
927
928 // TODO: Check if any of these functions accept null parameters
929 // If they do, I don't need to check for that
930
931 if (uiOverlay != nullptr) {
932 SDL_DestroyTexture(uiOverlay);
933 uiOverlay = nullptr;
934 }
935
936 if (fontSDLTexture != nullptr) {
937 SDL_DestroyTexture(fontSDLTexture);
938 fontSDLTexture = nullptr;
939 }
940
941 if (imageSDLTexture != nullptr) {
942 SDL_DestroyTexture(imageSDLTexture);
943 imageSDLTexture = nullptr;
944 }
945
946 TTF_CloseFont(font);
947 font = nullptr;
948
949 SDL_DestroyRenderer(renderer);
950 renderer = nullptr;
951
952 gui->destroyWindow();
953 gui->shutdown();
954 delete gui;
955}
956
957void VulkanGame::createVulkanInstance(const vector<const char*> &validationLayers) {
958 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
959 throw runtime_error("validation layers requested, but not available!");
960 }
961
962 VkApplicationInfo appInfo = {};
963 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
964 appInfo.pApplicationName = "Vulkan Game";
965 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
966 appInfo.pEngineName = "No Engine";
967 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
968 appInfo.apiVersion = VK_API_VERSION_1_0;
969
970 VkInstanceCreateInfo createInfo = {};
971 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
972 createInfo.pApplicationInfo = &appInfo;
973
974 vector<const char*> extensions = gui->getRequiredExtensions();
975 if (ENABLE_VALIDATION_LAYERS) {
976 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
977 }
978
979 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
980 createInfo.ppEnabledExtensionNames = extensions.data();
981
982 cout << endl << "Extensions:" << endl;
983 for (const char* extensionName : extensions) {
984 cout << extensionName << endl;
985 }
986 cout << endl;
987
988 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
989 if (ENABLE_VALIDATION_LAYERS) {
990 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
991 createInfo.ppEnabledLayerNames = validationLayers.data();
992
993 populateDebugMessengerCreateInfo(debugCreateInfo);
994 createInfo.pNext = &debugCreateInfo;
995 } else {
996 createInfo.enabledLayerCount = 0;
997
998 createInfo.pNext = nullptr;
999 }
1000
1001 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
1002 throw runtime_error("failed to create instance!");
1003 }
1004}
1005
1006void VulkanGame::setupDebugMessenger() {
1007 if (!ENABLE_VALIDATION_LAYERS) return;
1008
1009 VkDebugUtilsMessengerCreateInfoEXT createInfo;
1010 populateDebugMessengerCreateInfo(createInfo);
1011
1012 if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
1013 throw runtime_error("failed to set up debug messenger!");
1014 }
1015}
1016
1017void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
1018 createInfo = {};
1019 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
1020 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;
1021 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;
1022 createInfo.pfnUserCallback = debugCallback;
1023}
1024
1025VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
1026 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
1027 VkDebugUtilsMessageTypeFlagsEXT messageType,
1028 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
1029 void* pUserData) {
1030 cerr << "validation layer: " << pCallbackData->pMessage << endl;
1031
1032 return VK_FALSE;
1033}
1034
1035void VulkanGame::createVulkanSurface() {
1036 if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
1037 throw runtime_error("failed to create window surface!");
1038 }
1039}
1040
1041void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
1042 uint32_t deviceCount = 0;
1043 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
1044
1045 if (deviceCount == 0) {
1046 throw runtime_error("failed to find GPUs with Vulkan support!");
1047 }
1048
1049 vector<VkPhysicalDevice> devices(deviceCount);
1050 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
1051
1052 cout << endl << "Graphics cards:" << endl;
1053 for (const VkPhysicalDevice& device : devices) {
1054 if (isDeviceSuitable(device, deviceExtensions)) {
1055 physicalDevice = device;
1056 break;
1057 }
1058 }
1059 cout << endl;
1060
1061 if (physicalDevice == VK_NULL_HANDLE) {
1062 throw runtime_error("failed to find a suitable GPU!");
1063 }
1064}
1065
1066bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice,
1067 const vector<const char*>& deviceExtensions) {
1068 VkPhysicalDeviceProperties deviceProperties;
1069 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
1070
1071 cout << "Device: " << deviceProperties.deviceName << endl;
1072
1073 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1074 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
1075 bool swapChainAdequate = false;
1076
1077 if (extensionsSupported) {
1078 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
1079 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
1080 }
1081
1082 VkPhysicalDeviceFeatures supportedFeatures;
1083 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
1084
1085 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
1086}
1087
1088void VulkanGame::createLogicalDevice(
1089 const vector<const char*> validationLayers, const vector<const char*>& deviceExtensions) {
1090 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1091
1092 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
1093 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1094
1095 float queuePriority = 1.0f;
1096 for (uint32_t queueFamily : uniqueQueueFamilies) {
1097 VkDeviceQueueCreateInfo queueCreateInfo = {};
1098 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1099 queueCreateInfo.queueFamilyIndex = queueFamily;
1100 queueCreateInfo.queueCount = 1;
1101 queueCreateInfo.pQueuePriorities = &queuePriority;
1102
1103 queueCreateInfoList.push_back(queueCreateInfo);
1104 }
1105
1106 VkPhysicalDeviceFeatures deviceFeatures = {};
1107 deviceFeatures.samplerAnisotropy = VK_TRUE;
1108
1109 VkDeviceCreateInfo createInfo = {};
1110 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
1111 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
1112 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
1113
1114 createInfo.pEnabledFeatures = &deviceFeatures;
1115
1116 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
1117 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
1118
1119 // These fields are ignored by up-to-date Vulkan implementations,
1120 // but it's a good idea to set them for backwards compatibility
1121 if (ENABLE_VALIDATION_LAYERS) {
1122 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
1123 createInfo.ppEnabledLayerNames = validationLayers.data();
1124 } else {
1125 createInfo.enabledLayerCount = 0;
1126 }
1127
1128 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
1129 throw runtime_error("failed to create logical device!");
1130 }
1131
1132 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
1133 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
1134}
1135
1136void VulkanGame::createSwapChain() {
1137 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
1138
1139 VkSurfaceFormatKHR surfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(swapChainSupport.formats);
1140 VkPresentModeKHR presentMode = VulkanUtils::chooseSwapPresentMode(swapChainSupport.presentModes);
1141 VkExtent2D extent = VulkanUtils::chooseSwapExtent(swapChainSupport.capabilities, gui->getWindowWidth(), gui->getWindowHeight());
1142
1143 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
1144 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
1145 imageCount = swapChainSupport.capabilities.maxImageCount;
1146 }
1147
1148 VkSwapchainCreateInfoKHR createInfo = {};
1149 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1150 createInfo.surface = surface;
1151 createInfo.minImageCount = imageCount;
1152 createInfo.imageFormat = surfaceFormat.format;
1153 createInfo.imageColorSpace = surfaceFormat.colorSpace;
1154 createInfo.imageExtent = extent;
1155 createInfo.imageArrayLayers = 1;
1156 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1157
1158 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
1159 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
1160
1161 if (indices.graphicsFamily != indices.presentFamily) {
1162 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
1163 createInfo.queueFamilyIndexCount = 2;
1164 createInfo.pQueueFamilyIndices = queueFamilyIndices;
1165 } else {
1166 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1167 createInfo.queueFamilyIndexCount = 0;
1168 createInfo.pQueueFamilyIndices = nullptr;
1169 }
1170
1171 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
1172 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1173 createInfo.presentMode = presentMode;
1174 createInfo.clipped = VK_TRUE;
1175 createInfo.oldSwapchain = VK_NULL_HANDLE;
1176
1177 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
1178 throw runtime_error("failed to create swap chain!");
1179 }
1180
1181 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
1182 swapChainImages.resize(imageCount);
1183 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
1184
1185 swapChainImageFormat = surfaceFormat.format;
1186 swapChainExtent = extent;
1187}
1188
1189void VulkanGame::createImageViews() {
1190 swapChainImageViews.resize(swapChainImages.size());
1191
1192 for (size_t i = 0; i < swapChainImages.size(); i++) {
1193 swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainImageFormat,
1194 VK_IMAGE_ASPECT_COLOR_BIT);
1195 }
1196}
1197
1198void VulkanGame::createRenderPass() {
1199 VkAttachmentDescription colorAttachment = {};
1200 colorAttachment.format = swapChainImageFormat;
1201 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1202 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1203 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1204 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1205 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1206 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1207 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1208
1209 VkAttachmentReference colorAttachmentRef = {};
1210 colorAttachmentRef.attachment = 0;
1211 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1212
1213 VkAttachmentDescription depthAttachment = {};
1214 depthAttachment.format = findDepthFormat();
1215 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
1216 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1217 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1218 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1219 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1220 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1221 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1222
1223 VkAttachmentReference depthAttachmentRef = {};
1224 depthAttachmentRef.attachment = 1;
1225 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1226
1227 VkSubpassDescription subpass = {};
1228 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1229 subpass.colorAttachmentCount = 1;
1230 subpass.pColorAttachments = &colorAttachmentRef;
1231 subpass.pDepthStencilAttachment = &depthAttachmentRef;
1232
1233 VkSubpassDependency dependency = {};
1234 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1235 dependency.dstSubpass = 0;
1236 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1237 dependency.srcAccessMask = 0;
1238 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1239 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1240
1241 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
1242 VkRenderPassCreateInfo renderPassInfo = {};
1243 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1244 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1245 renderPassInfo.pAttachments = attachments.data();
1246 renderPassInfo.subpassCount = 1;
1247 renderPassInfo.pSubpasses = &subpass;
1248 renderPassInfo.dependencyCount = 1;
1249 renderPassInfo.pDependencies = &dependency;
1250
1251 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
1252 throw runtime_error("failed to create render pass!");
1253 }
1254}
1255
1256VkFormat VulkanGame::findDepthFormat() {
1257 return VulkanUtils::findSupportedFormat(
1258 physicalDevice,
1259 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
1260 VK_IMAGE_TILING_OPTIMAL,
1261 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1262 );
1263}
1264
1265void VulkanGame::createCommandPool() {
1266 QueueFamilyIndices queueFamilyIndices = VulkanUtils::findQueueFamilies(physicalDevice, surface);;
1267
1268 VkCommandPoolCreateInfo poolInfo = {};
1269 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1270 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
1271 poolInfo.flags = 0;
1272
1273 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
1274 throw runtime_error("failed to create graphics command pool!");
1275 }
1276}
1277
1278void VulkanGame::createImageResources() {
1279 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
1280 depthImage, graphicsQueue);
1281
1282 createTextureSampler();
1283
1284 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, commandPool, "textures/texture.jpg",
1285 floorTextureImage, graphicsQueue);
1286
1287 floorTextureImageDescriptor = {};
1288 floorTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1289 floorTextureImageDescriptor.imageView = floorTextureImage.imageView;
1290 floorTextureImageDescriptor.sampler = textureSampler;
1291
1292 VulkanUtils::createVulkanImageFromSDLTexture(device, physicalDevice, uiOverlay, sdlOverlayImage);
1293
1294 sdlOverlayImageDescriptor = {};
1295 sdlOverlayImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1296 sdlOverlayImageDescriptor.imageView = sdlOverlayImage.imageView;
1297 sdlOverlayImageDescriptor.sampler = textureSampler;
1298}
1299
1300void VulkanGame::createTextureSampler() {
1301 VkSamplerCreateInfo samplerInfo = {};
1302 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1303 samplerInfo.magFilter = VK_FILTER_LINEAR;
1304 samplerInfo.minFilter = VK_FILTER_LINEAR;
1305
1306 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1307 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1308 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1309
1310 samplerInfo.anisotropyEnable = VK_TRUE;
1311 samplerInfo.maxAnisotropy = 16;
1312 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
1313 samplerInfo.unnormalizedCoordinates = VK_FALSE;
1314 samplerInfo.compareEnable = VK_FALSE;
1315 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
1316 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
1317 samplerInfo.mipLodBias = 0.0f;
1318 samplerInfo.minLod = 0.0f;
1319 samplerInfo.maxLod = 0.0f;
1320
1321 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
1322 throw runtime_error("failed to create texture sampler!");
1323 }
1324}
1325
1326void VulkanGame::createFramebuffers() {
1327 swapChainFramebuffers.resize(swapChainImageViews.size());
1328
1329 for (size_t i = 0; i < swapChainImageViews.size(); i++) {
1330 array<VkImageView, 2> attachments = {
1331 swapChainImageViews[i],
1332 depthImage.imageView
1333 };
1334
1335 VkFramebufferCreateInfo framebufferInfo = {};
1336 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1337 framebufferInfo.renderPass = renderPass;
1338 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
1339 framebufferInfo.pAttachments = attachments.data();
1340 framebufferInfo.width = swapChainExtent.width;
1341 framebufferInfo.height = swapChainExtent.height;
1342 framebufferInfo.layers = 1;
1343
1344 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
1345 throw runtime_error("failed to create framebuffer!");
1346 }
1347 }
1348}
1349
1350void VulkanGame::createCommandBuffers() {
1351 commandBuffers.resize(swapChainImages.size());
1352
1353 VkCommandBufferAllocateInfo allocInfo = {};
1354 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1355 allocInfo.commandPool = commandPool;
1356 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1357 allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
1358
1359 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
1360 throw runtime_error("failed to allocate command buffers!");
1361 }
1362
1363 for (size_t i = 0; i < commandBuffers.size(); i++) {
1364 VkCommandBufferBeginInfo beginInfo = {};
1365 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1366 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
1367 beginInfo.pInheritanceInfo = nullptr;
1368
1369 if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
1370 throw runtime_error("failed to begin recording command buffer!");
1371 }
1372
1373 VkRenderPassBeginInfo renderPassInfo = {};
1374 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1375 renderPassInfo.renderPass = renderPass;
1376 renderPassInfo.framebuffer = swapChainFramebuffers[i];
1377 renderPassInfo.renderArea.offset = { 0, 0 };
1378 renderPassInfo.renderArea.extent = swapChainExtent;
1379
1380 array<VkClearValue, 2> clearValues = {};
1381 clearValues[0].color = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
1382 clearValues[1].depthStencil = { 1.0f, 0 };
1383
1384 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
1385 renderPassInfo.pClearValues = clearValues.data();
1386
1387 vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1388
1389 modelPipeline.createRenderCommands(commandBuffers[i], i);
1390 shipPipeline.createRenderCommands(commandBuffers[i], i);
1391 asteroidPipeline.createRenderCommands(commandBuffers[i], i);
1392
1393 // Always render this pipeline last
1394 overlayPipeline.createRenderCommands(commandBuffers[i], i);
1395
1396 vkCmdEndRenderPass(commandBuffers[i]);
1397
1398 if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
1399 throw runtime_error("failed to record command buffer!");
1400 }
1401 }
1402}
1403
1404void VulkanGame::createSyncObjects() {
1405 imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1406 renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1407 inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
1408
1409 VkSemaphoreCreateInfo semaphoreInfo = {};
1410 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1411
1412 VkFenceCreateInfo fenceInfo = {};
1413 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1414 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1415
1416 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1417 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
1418 vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
1419 vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
1420 throw runtime_error("failed to create synchronization objects for a frame!");
1421 }
1422 }
1423}
1424
1425void VulkanGame::createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags flags,
1426 vector<VkBuffer>& buffers, vector<VkDeviceMemory>& buffersMemory, vector<VkDescriptorBufferInfo>& bufferInfoList) {
1427 buffers.resize(swapChainImages.size());
1428 buffersMemory.resize(swapChainImages.size());
1429 bufferInfoList.resize(swapChainImages.size());
1430
1431 for (size_t i = 0; i < swapChainImages.size(); i++) {
1432 VulkanUtils::createBuffer(device, physicalDevice, bufferSize, flags,
1433 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1434 buffers[i], buffersMemory[i]);
1435
1436 bufferInfoList[i].buffer = buffers[i];
1437 bufferInfoList[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
1438 bufferInfoList[i].range = bufferSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
1439 }
1440}
1441
1442// TODO: Fix the crash that happens when alt-tabbing
1443void VulkanGame::recreateSwapChain() {
1444 cout << "Recreating swap chain" << endl;
1445 gui->refreshWindowSize();
1446
1447 while (gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0 ||
1448 (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0) {
1449 SDL_WaitEvent(nullptr);
1450 gui->refreshWindowSize();
1451 }
1452
1453 vkDeviceWaitIdle(device);
1454
1455 cleanupSwapChain();
1456
1457 createSwapChain();
1458 createImageViews();
1459 createRenderPass();
1460
1461 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
1462 depthImage, graphicsQueue);
1463 createFramebuffers();
1464
1465 // TODO: Move UBO creation/management into GraphicsPipeline_Vulkan, like I did with SSBOs
1466
1467 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1468 uniformBuffers_modelPipeline, uniformBuffersMemory_modelPipeline, uniformBufferInfoList_modelPipeline);
1469
1470 modelPipeline.updateRenderPass(renderPass);
1471 modelPipeline.createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
1472 modelPipeline.createDescriptorPool(swapChainImages);
1473 modelPipeline.createDescriptorSets(swapChainImages);
1474
1475 overlayPipeline.updateRenderPass(renderPass);
1476 overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
1477 overlayPipeline.createDescriptorPool(swapChainImages);
1478 overlayPipeline.createDescriptorSets(swapChainImages);
1479
1480 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1481 uniformBuffers_shipPipeline, uniformBuffersMemory_shipPipeline, uniformBufferInfoList_shipPipeline);
1482
1483 shipPipeline.updateRenderPass(renderPass);
1484 shipPipeline.createPipeline("shaders/ship-vert.spv", "shaders/ship-frag.spv");
1485 shipPipeline.createDescriptorPool(swapChainImages);
1486 shipPipeline.createDescriptorSets(swapChainImages);
1487
1488 createBufferSet(sizeof(UBO_VP_mats), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1489 uniformBuffers_asteroidPipeline, uniformBuffersMemory_asteroidPipeline, uniformBufferInfoList_asteroidPipeline);
1490
1491 asteroidPipeline.updateRenderPass(renderPass);
1492 asteroidPipeline.createPipeline("shaders/asteroid-vert.spv", "shaders/asteroid-frag.spv");
1493 asteroidPipeline.createDescriptorPool(swapChainImages);
1494 asteroidPipeline.createDescriptorSets(swapChainImages);
1495
1496 createCommandBuffers();
1497}
1498
1499void VulkanGame::cleanupSwapChain() {
1500 VulkanUtils::destroyVulkanImage(device, depthImage);
1501
1502 for (VkFramebuffer framebuffer : swapChainFramebuffers) {
1503 vkDestroyFramebuffer(device, framebuffer, nullptr);
1504 }
1505
1506 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
1507
1508 overlayPipeline.cleanup();
1509 modelPipeline.cleanup();
1510 shipPipeline.cleanup();
1511 asteroidPipeline.cleanup();
1512
1513 for (size_t i = 0; i < uniformBuffers_modelPipeline.size(); i++) {
1514 vkDestroyBuffer(device, uniformBuffers_modelPipeline[i], nullptr);
1515 vkFreeMemory(device, uniformBuffersMemory_modelPipeline[i], nullptr);
1516 }
1517
1518 for (size_t i = 0; i < uniformBuffers_shipPipeline.size(); i++) {
1519 vkDestroyBuffer(device, uniformBuffers_shipPipeline[i], nullptr);
1520 vkFreeMemory(device, uniformBuffersMemory_shipPipeline[i], nullptr);
1521 }
1522
1523 for (size_t i = 0; i < uniformBuffers_asteroidPipeline.size(); i++) {
1524 vkDestroyBuffer(device, uniformBuffers_asteroidPipeline[i], nullptr);
1525 vkFreeMemory(device, uniformBuffersMemory_asteroidPipeline[i], nullptr);
1526 }
1527
1528 vkDestroyRenderPass(device, renderPass, nullptr);
1529
1530 for (VkImageView imageView : swapChainImageViews) {
1531 vkDestroyImageView(device, imageView, nullptr);
1532 }
1533
1534 vkDestroySwapchainKHR(device, swapChain, nullptr);
1535}
Note: See TracBrowser for help on using the repository browser.