source: opengl-game/vulkan-game.cpp@ 055750a

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

In VulkanGame, use SSBOs in the ship and scene shaders to store per-object data (currently just the model matrix)

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