source: opengl-game/vulkan-game.cpp@ 860a0da

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

In VulkanGame, move fields related to the ssbo, as well as code to create thee ssbo, destroy it, and create a descriptor for it, into GraphicsPipeline_Vulkan

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