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

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

In VulkanGame, add an objectIndex vertex attribute to the ship shader so it can be used as an index into the ssbo object array

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