source: opengl-game/vulkan-game.cpp@ 22217d4

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

Make the view and projection matrices instaces variables of the VulkanGame class

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