source: opengl-game/vulkan-game.cpp@ 0fe8433

feature/imgui-sdl points-test
Last change on this file since 0fe8433 was 0fe8433, checked in by Dmitry Portnoy <dmp1488@…>, 5 years ago

Create an addObject() method in VulkanGame (which wraps the old addObject() method in GraphicsPipeline_Vulkan), and move the list of objects and other code to manage objects from the pipeline to the VulkanGame class

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