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

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

In VulkanGame, add objIndex to scene objects, use it in the scene shader to index into the ssbo, and change the code that copies data to the ssbo to do so for each scene object, not just the first one

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