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

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

In VulkanGame, add an ssbo field to SceneObject to hold per-object ssbo info, and add a new SSBOType template parameter to SceneObject and GraphicsPipeline_Vulkan

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