source: opengl-game/vulkan-game.cpp@ 3782d66

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

Add a new pipeline, vertex type, and ubo-related structures to render the ship

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