source: opengl-game/vulkan-game.cpp@ 237cbec

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

Create a pipeline and shaders to render multicolored lasers

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