source: opengl-game/vulkan-game.cpp@ 7c929fc

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

In VulkanGame, make lighting work correctly in the ship shader before the MVP matrices are applied

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