source: opengl-game/vulkan-game.cpp@ 1add0ed

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

Set model_base and call updateObject()to control the initial position of the textured squares instead of manually changing the z-coordinate of their points

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