source: opengl-game/vulkan-game.cpp@ 785333b

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

Setup debug environment for ship shader lighting

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