source: opengl-game/vulkan-game.cpp@ 3e8cc8b

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

In VulkanGame, add the asteroid shader pipeline and start implementing asteroids flying at the player from the top of the screen

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