source: opengl-game/vulkan-game.cpp@ 2b40f48

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

Merge branch 'master' into hotfix/ship-shader-lights

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