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

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

In VulkanGame, make the ship move when the player holds down the right or left arrow keys

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