source: opengl-game/vulkan-game.cpp@ a79be34

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

Finish copying the ship pipeline to VulkanGame

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