source: opengl-game/vulkan-game.cpp@ e1308e8

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

In VulkanGame, add normals to the ship pipeline and get lighting to work

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