source: opengl-game/vulkan-game.cpp@ 4ece3bf

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

In VulkanGame, add the ability to delete asteroids and move them at a constant speed

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