source: opengl-game/vulkan-game.cpp@ 44f23af

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

In VulkanGame, add code to resize the storage buffer and update the right descriptor info when the storage buffer becomes full

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