source: opengl-game/vulkan-game.cpp@ 4994692

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

Change VulkanGame::addObject() to return a reference to the newly-created object

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