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

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

In VulkanGame, move the logic of updating per-object data in the SSBO into GraphicsPipeline_Vulkan and remove the SSBO properties from VulkanGame

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