source: opengl-game/vulkan-game.cpp@ 3b84bb6

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

In VulkanGame, call centerObject() on all objects when they are created and move that call inside VulkanGame::addObject(), rename GraphicsPipeline_Vulkan::addVertices() to addObject, add start adding generic support for objects to be added after pipeline creation is complete

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