source: opengl-game/sdl-game.hpp@ 6bac215

feature/imgui-sdl
Last change on this file since 6bac215 was 6bac215, checked in by Dmitry Portnoy <dportnoy@…>, 4 years ago

Rewrite a large portion of the VulkanBuffer class, start using it more
to resize buffers, and simplify resizeBufferSet() since the additions to
VulkanBuffer can now do some of what that function used to do

  • Property mode set to 100644
File size: 16.9 KB
RevLine 
[3b7d497]1#ifndef _SDL_GAME_H
2#define _SDL_GAME_H
3
[40eb092]4#include <chrono>
5#include <map>
[3b7d497]6#include <vector>
[40eb092]7
[3b7d497]8#include <vulkan/vulkan.h>
9
10#include <SDL2/SDL.h>
[c6f0793]11
[4a777d2]12#define GLM_FORCE_RADIANS
13#define GLM_FORCE_DEPTH_ZERO_TO_ONE // Since, in Vulkan, the depth range is 0 to 1 instead of -1 to 1
14#define GLM_FORCE_RIGHT_HANDED
15
16#include <glm/glm.hpp>
17#include <glm/gtc/matrix_transform.hpp>
18
[6493e43]19#include "IMGUI/imgui_impl_vulkan.h"
20
[3b7d497]21#include "consts.hpp"
[8aa4888]22#include "game-gui-sdl.hpp"
[ce9dc9f]23#include "vulkan-utils.hpp"
[4a777d2]24#include "graphics-pipeline_vulkan.hpp"
[8aa4888]25#include "vulkan-buffer.hpp"
[3b7d497]26
[4a777d2]27using namespace glm;
[3b7d497]28using namespace std;
[40eb092]29using namespace std::chrono;
[3b7d497]30
31#define VulkanGame NewVulkanGame
32
33#ifdef NDEBUG
34 const bool ENABLE_VALIDATION_LAYERS = false;
35#else
36 const bool ENABLE_VALIDATION_LAYERS = true;
37#endif
38
[4a777d2]39// TODO: Consider if there is a better way of dealing with all the vertex types and ssbo types, maybe
40// by consolidating some and trying to keep new ones to a minimum
41
42struct ModelVertex {
43 vec3 pos;
44 vec3 color;
45 vec2 texCoord;
46 vec3 normal;
47 unsigned int objIndex;
48};
49
50struct LaserVertex {
51 vec3 pos;
52 vec2 texCoord;
53 unsigned int objIndex;
54};
55
56struct ExplosionVertex {
57 vec3 particleStartVelocity;
58 float particleStartTime;
59 unsigned int objIndex;
60};
61
[8aa4888]62// Currently using these as the dynamic UBO types as well
63// TODO: Rename them to something more general
64
[4a777d2]65struct SSBO_ModelObject {
66 alignas(16) mat4 model;
67};
68
69struct SSBO_Asteroid {
70 alignas(16) mat4 model;
71 alignas(4) float hp;
72 alignas(4) unsigned int deleted;
73};
74
75struct UBO_VP_mats {
76 alignas(16) mat4 view;
77 alignas(16) mat4 proj;
78};
79
[996dd3e]80// TODO: Use this struct for uniform buffers as well and probably combine it with the VulkanBuffer class
81// Also, probably better to make this a vector of structs where each struct
82// has a VkBuffer, VkDeviceMemory, and VkDescriptorBufferInfo
[a3cefaa]83// TODO: Maybe change the structure here since VkDescriptorBufferInfo already stores a reference to the VkBuffer
[1abebc1]84struct BufferSet {
[996dd3e]85 vector<VkBuffer> buffers;
86 vector<VkDeviceMemory> memory;
87 vector<VkDescriptorBufferInfo> infoSet;
[8aa4888]88 VkBufferUsageFlags usages;
89 VkMemoryPropertyFlags properties;
[996dd3e]90};
91
[4a777d2]92// TODO: Change the index type to uint32_t and check the Vulkan Tutorial loading model section as a reference
93// TODO: Create a typedef for index type so I can easily change uin16_t to something else later
94// TODO: Maybe create a typedef for each of the templated SceneObject types
95template<class VertexType, class SSBOType>
96struct SceneObject {
97 vector<VertexType> vertices;
98 vector<uint16_t> indices;
99 SSBOType ssbo;
100
101 mat4 model_base;
102 mat4 model_transform;
103
104 bool modified;
105
106 // TODO: Figure out if I should make child classes that have these fields instead of putting them in the
107 // parent class
108 vec3 center; // currently only matters for asteroids
109 float radius; // currently only matters for asteroids
110 SceneObject<ModelVertex, SSBO_Asteroid>* targetAsteroid; // currently only used for lasers
111};
112
113// TODO: Have to figure out how to include an optional ssbo parameter for each object
114// Could probably use the same approach to make indices optional
115// Figure out if there are sufficient use cases to make either of these optional or is it fine to make
116// them mamdatory
117
118
119// TODO: Look into using dynamic_cast to check types of SceneObject and EffectOverTime
120
[40eb092]121// TODO: Maybe move this to a different header
122
123enum UIValueType {
124 UIVALUE_INT,
125 UIVALUE_DOUBLE,
126};
127
128struct UIValue {
129 UIValueType type;
130 string label;
131 void* value;
132
133 UIValue(UIValueType _type, string _label, void* _value) : type(_type), label(_label), value(_value) {}
134};
135
[3b7d497]136class VulkanGame {
[914bb99]137
[3b7d497]138 public:
[914bb99]139
[ce9dc9f]140 VulkanGame();
[3b7d497]141 ~VulkanGame();
142
[ce9dc9f]143 void run(int width, int height, unsigned char guiFlags);
[3b7d497]144
145 private:
[914bb99]146
[3b7d497]147 static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
148 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
149 VkDebugUtilsMessageTypeFlagsEXT messageType,
150 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
151 void* pUserData);
152
[4a777d2]153 // TODO: Maybe pass these in as parameters to some Camera class
154 const float NEAR_CLIP = 0.1f;
155 const float FAR_CLIP = 100.0f;
156 const float FOV_ANGLE = 67.0f; // means the camera lens goes from -33 deg to 33 deg
157
[c6f0793]158 bool done;
159
[4a777d2]160 vec3 cam_pos;
161
[3b7d497]162 // TODO: Good place to start using smart pointers
163 GameGui* gui;
164
165 SDL_version sdlVersion;
166 SDL_Window* window;
167
[ce9dc9f]168 VkInstance instance;
[7865c5b]169 VkDebugUtilsMessengerEXT debugMessenger;
170 VkSurfaceKHR vulkanSurface;
171 VkPhysicalDevice physicalDevice;
[ce9dc9f]172 VkDevice device;
[6493e43]173
174 VkQueue graphicsQueue;
175 VkQueue presentQueue;
[ce9dc9f]176
177 // TODO: Maybe make a swapchain struct for convenience
178 VkSurfaceFormatKHR swapChainSurfaceFormat;
179 VkPresentModeKHR swapChainPresentMode;
180 VkExtent2D swapChainExtent;
181 uint32_t swapChainMinImageCount;
182 uint32_t swapChainImageCount;
183
184 VkSwapchainKHR swapChain;
185 vector<VkImage> swapChainImages;
186 vector<VkImageView> swapChainImageViews;
187 vector<VkFramebuffer> swapChainFramebuffers;
188
189 VkRenderPass renderPass;
190
191 VkCommandPool resourceCommandPool;
192
193 vector<VkCommandPool> commandPools;
194 vector<VkCommandBuffer> commandBuffers;
195
196 VulkanImage depthImage;
197
198 // These are per frame
199 vector<VkSemaphore> imageAcquiredSemaphores;
200 vector<VkSemaphore> renderCompleteSemaphores;
201
202 // These are per swap chain image
203 vector<VkFence> inFlightFences;
204
205 uint32_t imageIndex;
206 uint32_t currentFrame;
207
[28ea92f]208 bool shouldRecreateSwapChain;
209
[4a777d2]210 VkSampler textureSampler;
211
212 VulkanImage floorTextureImage;
213 VkDescriptorImageInfo floorTextureImageDescriptor;
214
215 mat4 viewMat, projMat;
216
[e469aed]217 // Maybe at some point create an imgui pipeline class, but I don't think it makes sense right now
218 VkDescriptorPool imguiDescriptorPool;
219
[4a777d2]220 // TODO: Probably restructure the GraphicsPipeline_Vulkan class based on what I learned about descriptors and textures
221 // while working on graphics-library. Double-check exactly what this was and note it down here.
222 // Basically, I think the point was that if I have several models that all use the same shaders and, therefore,
223 // the same pipeline, but use different textures, the approach I took when initially creating GraphicsPipeline_Vulkan
224 // wouldn't work since the whole pipeline couldn't have a common set of descriptors for the textures
[9d21aac]225 GraphicsPipeline_Vulkan<ModelVertex> modelPipeline;
[4a777d2]226
[1abebc1]227 BufferSet storageBuffers_modelPipeline;
[a3cefaa]228 VulkanBuffer<SSBO_ModelObject> objects_modelPipeline;
[c163d81]229 BufferSet uniformBuffers_modelPipeline;
[996dd3e]230
[4a777d2]231 // TODO: Maybe make the ubo objects part of the pipeline class since there's only one ubo
232 // per pipeline.
233 // Or maybe create a higher level wrapper around GraphicsPipeline_Vulkan to hold things like
234 // the objects vector, the ubo, and the ssbo
235
236 // TODO: Rename *_VP_mats to *_uniforms and possibly use different types for each one
237 // if there is a need to add other uniform variables to one or more of the shaders
238
239 vector<SceneObject<ModelVertex, SSBO_ModelObject>> modelObjects;
240
241 UBO_VP_mats object_VP_mats;
242
[40eb092]243 /*** High-level vars ***/
244
[85b5fec]245 // TODO: Just typedef the type of this function to RenderScreenFn or something since it's used in a few places
246 void (VulkanGame::* currentRenderScreenFn)(int width, int height);
[40eb092]247
248 map<string, vector<UIValue>> valueLists;
249
250 int score;
251 float fps;
252
253 // TODO: Make a separate singleton Timer class
254 time_point<steady_clock> startTime;
[5081b9a]255 float fpsStartTime, curTime, prevTime, elapsedTime;
[40eb092]256
257 int frameCount;
258
259 /*** Functions ***/
260
[3b7d497]261 bool initUI(int width, int height, unsigned char guiFlags);
[40eb092]262 void initVulkan();
[4a777d2]263 void initGraphicsPipelines();
264 void initMatrices();
[40eb092]265 void renderLoop();
[4a777d2]266 void updateScene();
[40eb092]267 void cleanup();
[3b7d497]268
269 void createVulkanInstance(const vector<const char*>& validationLayers);
270 void setupDebugMessenger();
271 void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo);
272 void createVulkanSurface();
[ce9dc9f]273 void pickPhysicalDevice(const vector<const char*>& deviceExtensions);
[3b7d497]274 bool isDeviceSuitable(VkPhysicalDevice physicalDevice, const vector<const char*>& deviceExtensions);
275 void createLogicalDevice(const vector<const char*>& validationLayers,
[ce9dc9f]276 const vector<const char*>& deviceExtensions);
277 void chooseSwapChainProperties();
278 void createSwapChain();
279 void createImageViews();
280 void createResourceCommandPool();
[e469aed]281 void createImageResources();
282 VkFormat findDepthFormat(); // TODO: Declare/define (in the cpp file) this function in some util functions section
283 void createRenderPass();
[ce9dc9f]284 void createCommandPools();
285 void createFramebuffers();
286 void createCommandBuffers();
287 void createSyncObjects();
288
[4a777d2]289 void createTextureSampler();
290
[e469aed]291 void initImGuiOverlay();
292 void cleanupImGuiOverlay();
293
[9d21aac]294 // TODO: Maybe move these to a different class, possibly VulkanBuffer or some new related class
295
[8aa4888]296 void createBufferSet(VkDeviceSize bufferSize, VkBufferUsageFlags usages, VkMemoryPropertyFlags properties,
[c163d81]297 BufferSet& set);
[4a777d2]298
[9d21aac]299 template<class VertexType, class SSBOType>
[1abebc1]300 void resizeBufferSet(BufferSet& set, VulkanBuffer<SSBOType>& buffer,
301 GraphicsPipeline_Vulkan<VertexType>& pipeline, VkCommandPool commandPool,
302 VkQueue graphicsQueue);
[9d21aac]303
304 template<class SSBOType>
[1abebc1]305 void updateBufferSet(BufferSet& set, size_t objIndex, SSBOType& ssbo);
[9d21aac]306
[4a777d2]307 // TODO: Since addObject() returns a reference to the new object now,
308 // stop using objects.back() to access the object that was just created
309 template<class VertexType, class SSBOType>
[b8072d3]310 SceneObject<VertexType, SSBOType>& addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
[9d21aac]311 GraphicsPipeline_Vulkan<VertexType>& pipeline,
[b8072d3]312 const vector<VertexType>& vertices, vector<uint16_t> indices,
[8dcbf62]313 VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo);
[4a777d2]314
315 template<class VertexType>
316 vector<VertexType> addObjectIndex(unsigned int objIndex, vector<VertexType> vertices);
317
318 template<class VertexType>
319 vector<VertexType> addVertexNormals(vector<VertexType> vertices);
320
321 template<class VertexType, class SSBOType>
322 void centerObject(SceneObject<VertexType, SSBOType>& object);
323
324 template<class VertexType, class SSBOType>
[1abebc1]325 void updateObject(SceneObject<VertexType, SSBOType>& obj);
[4a777d2]326
[4e2c709]327 void renderFrame(ImDrawData* draw_data);
328 void presentFrame();
329
[ce9dc9f]330 void recreateSwapChain();
331
332 void cleanupSwapChain();
[6493e43]333
[40eb092]334 /*** High-level functions ***/
335
[85b5fec]336 void renderMainScreen(int width, int height);
337 void renderGameScreen(int width, int height);
[40eb092]338
339 void initGuiValueLists(map<string, vector<UIValue>>& valueLists);
340 void renderGuiValueList(vector<UIValue>& values);
341
[85b5fec]342 void goToScreen(void (VulkanGame::* renderScreenFn)(int width, int height));
[40eb092]343 void quitGame();
[ce9dc9f]344};
[6493e43]345
[9d21aac]346template<class VertexType, class SSBOType>
[1abebc1]347void VulkanGame::resizeBufferSet(BufferSet& set, VulkanBuffer<SSBOType>& buffer,
348 GraphicsPipeline_Vulkan<VertexType>& pipeline, VkCommandPool commandPool,
349 VkQueue graphicsQueue) {
[6bac215]350 VkDeviceSize newSize = buffer.capacity * sizeof(SSBOType);
[9d21aac]351
352 for (size_t i = 0; i < set.buffers.size(); i++) {
[6bac215]353 VkBuffer newBuffer;
354 VkDeviceMemory newMemory;
[9d21aac]355
[6bac215]356 VulkanUtils::createBuffer(device, physicalDevice, newSize, set.usages, set.properties, newBuffer, newMemory);
[9d21aac]357
[6bac215]358 VulkanUtils::copyBuffer(device, commandPool, set.buffers[i], newBuffer, 0, 0, set.infoSet[i].range,
359 graphicsQueue);
[9d21aac]360
361 vkDestroyBuffer(device, set.buffers[i], nullptr);
362 vkFreeMemory(device, set.memory[i], nullptr);
363
[6bac215]364 set.buffers[i] = newBuffer;
365 set.memory[i] = newMemory;
[9d21aac]366
367 set.infoSet[i].buffer = set.buffers[i];
368 set.infoSet[i].offset = 0; // This is the offset from the start of the buffer, so always 0 for now
[6bac215]369 set.infoSet[i].range = newSize; // Size of the update starting from offset, or VK_WHOLE_SIZE
[9d21aac]370 }
[a3cefaa]371
372 // Assume the SSBO is always the 2nd binding
373 // TODO: Figure out a way to make this more flexible
[58453c3]374 pipeline.updateDescriptorInfo(1, &set.infoSet, swapChainImages.size());
[9d21aac]375}
376
377// TODO: See if it makes sense to pass in the current swapchain index instead of updating all of them
378template<class SSBOType>
[1abebc1]379void VulkanGame::updateBufferSet(BufferSet& set, size_t objIndex, SSBOType& ssbo) {
380 for (size_t i = 0; i < set.memory.size(); i++) {
[c074f81]381 VulkanUtils::copyDataToMemory(device, &ssbo, set.memory[i], objIndex * sizeof(SSBOType), sizeof(ssbo), false);
[9d21aac]382 }
383}
384
[4a777d2]385// TODO: Right now, it's basically necessary to pass the identity matrix in for ssbo.model
386// and to change the model matrix later by setting model_transform and then calling updateObject()
[9d21aac]387// Figure out a better way to allow the model matrix to be set during object creation
[4a777d2]388template<class VertexType, class SSBOType>
[9d21aac]389SceneObject<VertexType, SSBOType>& VulkanGame::addObject(vector<SceneObject<VertexType, SSBOType>>& objects,
390 GraphicsPipeline_Vulkan<VertexType>& pipeline,
391 const vector<VertexType>& vertices, vector<uint16_t> indices,
[8dcbf62]392 VulkanBuffer<SSBOType>& objectBuffer, SSBOType ssbo) {
[4a777d2]393 // TODO: Use the model field of ssbo to set the object's model_base
394 // currently, the passed in model is useless since it gets overridden in updateObject() anyway
395 size_t numVertices = pipeline.getNumVertices();
396
397 for (uint16_t& idx : indices) {
398 idx += numVertices;
399 }
400
401 objects.push_back({ vertices, indices, ssbo, mat4(1.0f), mat4(1.0f), false });
[8dcbf62]402 objectBuffer.add(ssbo);
[4a777d2]403
404 SceneObject<VertexType, SSBOType>& obj = objects.back();
405
406 // TODO: Specify whether to center the object outside of this function or, worst case, maybe
407 // with a boolean being passed in here, so that I don't have to rely on checking the specific object
408 // type
[8dcbf62]409 // TODO: Actually, I've already defined a no-op centerObject method for explosions
410 // Maybe I should do the same for lasers and remove this conditional altogether
[4a777d2]411 if (!is_same_v<VertexType, LaserVertex> && !is_same_v<VertexType, ExplosionVertex>) {
412 centerObject(obj);
413 }
414
[9d21aac]415 pipeline.addObject(obj.vertices, obj.indices, resourceCommandPool, graphicsQueue);
416
[4a777d2]417 return obj;
418}
419
420template<class VertexType>
421vector<VertexType> VulkanGame::addObjectIndex(unsigned int objIndex, vector<VertexType> vertices) {
422 for (VertexType& vertex : vertices) {
423 vertex.objIndex = objIndex;
424 }
425
426 return vertices;
427}
428
429template<class VertexType>
430vector<VertexType> VulkanGame::addVertexNormals(vector<VertexType> vertices) {
431 for (unsigned int i = 0; i < vertices.size(); i += 3) {
432 vec3 p1 = vertices[i].pos;
433 vec3 p2 = vertices[i + 1].pos;
434 vec3 p3 = vertices[i + 2].pos;
435
436 vec3 normal = normalize(cross(p2 - p1, p3 - p1));
437
438 // Add the same normal for all 3 vertices
439 vertices[i].normal = normal;
440 vertices[i + 1].normal = normal;
441 vertices[i + 2].normal = normal;
442 }
443
444 return vertices;
445}
446
447template<class VertexType, class SSBOType>
448void VulkanGame::centerObject(SceneObject<VertexType, SSBOType>& object) {
449 vector<VertexType>& vertices = object.vertices;
450
451 float min_x = vertices[0].pos.x;
452 float max_x = vertices[0].pos.x;
453 float min_y = vertices[0].pos.y;
454 float max_y = vertices[0].pos.y;
455 float min_z = vertices[0].pos.z;
456 float max_z = vertices[0].pos.z;
457
458 // start from the second point
459 for (unsigned int i = 1; i < vertices.size(); i++) {
460 vec3& pos = vertices[i].pos;
461
462 if (min_x > pos.x) {
463 min_x = pos.x;
464 }
465 else if (max_x < pos.x) {
466 max_x = pos.x;
467 }
468
469 if (min_y > pos.y) {
470 min_y = pos.y;
471 }
472 else if (max_y < pos.y) {
473 max_y = pos.y;
474 }
475
476 if (min_z > pos.z) {
477 min_z = pos.z;
478 }
479 else if (max_z < pos.z) {
480 max_z = pos.z;
481 }
482 }
483
484 vec3 center = vec3(min_x + max_x, min_y + max_y, min_z + max_z) / 2.0f;
485
486 for (unsigned int i = 0; i < vertices.size(); i++) {
487 vertices[i].pos -= center;
488 }
489
490 object.radius = std::max(max_x - center.x, max_y - center.y);
491 object.radius = std::max(object.radius, max_z - center.z);
492
493 object.center = vec3(0.0f, 0.0f, 0.0f);
494}
495
496// TODO: Just pass in the single object instead of a list of all of them
497template<class VertexType, class SSBOType>
[1abebc1]498void VulkanGame::updateObject(SceneObject<VertexType, SSBOType>& obj) {
[4a777d2]499 obj.ssbo.model = obj.model_transform * obj.model_base;
500 obj.center = vec3(obj.ssbo.model * vec4(0.0f, 0.0f, 0.0f, 1.0f));
501
502 obj.modified = false;
503}
504
[3b7d497]505#endif // _SDL_GAME_H
Note: See TracBrowser for help on using the repository browser.