├── VisibilityBufferTessellation ├── shaders │ ├── ui.frag.spv │ ├── ui.vert.spv │ ├── tessshade.frag.spv │ ├── tessshade.vert.spv │ ├── tesswrite.frag.spv │ ├── tesswrite.geom.spv │ ├── tesswrite.tesc.spv │ ├── tesswrite.tese.spv │ ├── tesswrite.vert.spv │ ├── visbuffshade.frag.spv │ ├── visbuffshade.vert.spv │ ├── visbuffwrite.frag.spv │ ├── visbuffwrite.vert.spv │ ├── glslangValidator.exe │ ├── ui.frag │ ├── ui.vert │ ├── tessshade.vert │ ├── visbuffshade.vert │ ├── tesswrite.vert │ ├── generate-spirv.bat │ ├── visbuffwrite.frag │ ├── visbuffwrite.vert │ ├── tesswrite.geom │ ├── tesswrite.tesc │ ├── tesswrite.tese │ ├── tesswrite.frag │ ├── visbuffshade.frag │ └── tessshade.frag ├── textures │ ├── grid.png │ ├── sand.jpg │ ├── rock.jpg │ ├── heightmap.png │ ├── sandheightmap.jpg │ └── sandnormals.png ├── main.cpp ├── Texture.h ├── imgui.ini ├── VBTTypes.h ├── DirectionalLight.h ├── PhysicalDevice.h ├── Buffer.h ├── DirectionalLight.cpp ├── Terrain.h ├── VisibilityBufferTessellation.sln ├── Camera.h ├── Image.h ├── SwapChain.h ├── Texture.cpp ├── VbtImGUI.h ├── Buffer.cpp ├── VulkanCore.h ├── Camera.cpp ├── Mesh.h ├── VbtUtils.h ├── Terrain.cpp ├── Mesh.cpp ├── PhysicalDevice.cpp ├── VulkanApplication.h ├── VisibilityBufferTessellation.vcxproj.filters ├── Image.cpp ├── SwapChain.cpp ├── VulkanCore.cpp ├── VbtImGUI.cpp └── VisibilityBufferTessellation.vcxproj ├── .gitattributes ├── README.md └── .gitignore /VisibilityBufferTessellation/shaders/ui.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/ui.frag.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/ui.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/ui.vert.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tessshade.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/tessshade.frag.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tessshade.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/tessshade.vert.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tesswrite.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/tesswrite.frag.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tesswrite.geom.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/tesswrite.geom.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tesswrite.tesc.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/tesswrite.tesc.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tesswrite.tese.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/tesswrite.tese.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tesswrite.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/tesswrite.vert.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/visbuffshade.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/visbuffshade.frag.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/visbuffshade.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/visbuffshade.vert.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/visbuffwrite.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/visbuffwrite.frag.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/visbuffwrite.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cammymcp/VisBufferTessellation/HEAD/VisibilityBufferTessellation/shaders/visbuffwrite.vert.spv -------------------------------------------------------------------------------- /VisibilityBufferTessellation/textures/grid.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:80b2f2e8708b8526e785cfd6211a739bf985e59e78d560363c1b2305191d2b1f 3 | size 4024 4 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/textures/sand.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:186de1126b260e9249df5819b2b789508f1de6aad017910c6d137ea3819cc7d8 3 | size 90551 4 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/textures/rock.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:aa3b153647398b7d1ba0aaf585f57d5832fdfee0fc1fcec4fae38093d4170c89 3 | size 965744 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.obj filter=lfs diff=lfs merge=lfs -text 2 | *.jpg filter=lfs diff=lfs merge=lfs -text 3 | *.png filter=lfs diff=lfs merge=lfs -text 4 | *.exe filter=lfs diff=lfs merge=lfs -text 5 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/textures/heightmap.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:79d4b9089618dd7c82c2ad6cd4bed13a2f8059dd968881b9a91c308e84157268 3 | size 5754561 4 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/textures/sandheightmap.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b6ec32e7b6955c63dbbe300ed9ad3f52fa113d6f1facff035a6b0062c1cbd76d 3 | size 62491 4 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/textures/sandnormals.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f7bc6cf848367aab50447e91b29326ed416958652fb45d0ab35bde1bcbee35b2 3 | size 1759444 4 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/glslangValidator.exe: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e5ebbc7989f0e6a996595854dfce07d232bfdc652678266d2e7a1bba3a9bc0dd 3 | size 4100408 4 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/ui.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout (binding = 0) uniform sampler2D fontSampler; 4 | 5 | layout (location = 0) in vec2 inUV; 6 | layout (location = 1) in vec4 inColor; 7 | 8 | layout (location = 0) out vec4 outColor; 9 | 10 | void main() 11 | { 12 | outColor = inColor * texture(fontSampler, inUV); 13 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "VulkanApplication.h" 4 | 5 | int main() 6 | { 7 | vbt::VulkanApplication application; 8 | 9 | try 10 | { 11 | application.Run(); 12 | } 13 | catch (const std::exception& e) 14 | { 15 | std::cerr << e.what() << std::endl; 16 | return EXIT_FAILURE; 17 | } 18 | //std::getchar(); 19 | return EXIT_SUCCESS; 20 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Texture.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXTURE_H 2 | #define TEXTURE_H 3 | 4 | #include "vk_mem_alloc.h" 5 | #include "Image.h" 6 | 7 | namespace vbt 8 | { 9 | // At the moment just a wrapper class for Image to allow loading data from file, may want to extend later 10 | class Texture: public Image 11 | { 12 | public: 13 | void LoadAndCreate(std::string path, VmaAllocator& allocator, VkDevice device, PhysicalDevice physDevice, VkCommandPool& cmdPool); 14 | }; 15 | } 16 | 17 | #endif -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/ui.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout (location = 0) in vec2 inPos; 4 | layout (location = 1) in vec2 inUV; 5 | layout (location = 2) in vec4 inColor; 6 | 7 | layout (push_constant) uniform PushConstants { 8 | vec2 scale; 9 | vec2 translate; 10 | } pushConstants; 11 | 12 | layout (location = 0) out vec2 outUV; 13 | layout (location = 1) out vec4 outColor; 14 | 15 | out gl_PerVertex 16 | { 17 | vec4 gl_Position; 18 | }; 19 | 20 | void main() 21 | { 22 | outUV = inUV; 23 | outColor = inColor; 24 | gl_Position = vec4(inPos * pushConstants.scale + pushConstants.translate, 0.0, 1.0); 25 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tessshade.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | 5 | // Out 6 | layout(location = 0) out vec2 outScreenPos; 7 | out gl_PerVertex 8 | { 9 | vec4 gl_Position; 10 | }; 11 | 12 | void main() 13 | { 14 | // Create a full screen triangle based on vertex index. 15 | vec4 position; 16 | position.x = (gl_VertexIndex == 2) ? 3.0f : -1.0f; 17 | position.y = (gl_VertexIndex == 0) ? -3.0f : 1.0f; 18 | position.zw = vec2(0.0f, 1.0f); 19 | 20 | outScreenPos = position.xy; 21 | gl_Position = position; 22 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/visbuffshade.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | 5 | // Out 6 | layout(location = 0) out vec2 outScreenPos; 7 | out gl_PerVertex 8 | { 9 | vec4 gl_Position; 10 | }; 11 | 12 | void main() 13 | { 14 | // Create a full screen triangle based on vertex index. 15 | vec4 position; 16 | position.x = (gl_VertexIndex == 2) ? 3.0f : -1.0f; 17 | position.y = (gl_VertexIndex == 0) ? -3.0f : 1.0f; 18 | position.zw = vec2(0.0f, 1.0f); 19 | 20 | outScreenPos = position.xy; 21 | gl_Position = position; 22 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tesswrite.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_ARB_shader_draw_parameters : enable 5 | 6 | // In 7 | layout(location = 0) in vec3 inPosition; 8 | layout(location = 1) in vec3 inNormal; 9 | layout(location = 2) in vec2 inTexCoords; 10 | 11 | // Out 12 | layout(location = 0) out vec2 outTexCoords; 13 | out gl_PerVertex 14 | { 15 | vec4 gl_Position; 16 | }; 17 | 18 | // Simple passthrough, only position is required. 19 | void main() 20 | { 21 | gl_Position = vec4(inPosition, 1.0); 22 | outTexCoords = inTexCoords; 23 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/imgui.ini: -------------------------------------------------------------------------------- 1 | [Window][Debug##Default] 2 | Pos=764,218 3 | Size=381,119 4 | Collapsed=0 5 | 6 | [Window][Example settings] 7 | Pos=17,53 8 | Size=350,200 9 | Collapsed=0 10 | 11 | [Window][ImGui Demo] 12 | Pos=38,36 13 | Size=547,669 14 | Collapsed=1 15 | 16 | [Window][Example: Console] 17 | Pos=60,60 18 | Size=520,600 19 | Collapsed=0 20 | 21 | [Window][App Settings and Measurements] 22 | Pos=8,12 23 | Size=375,200 24 | Collapsed=0 25 | 26 | [Window][Measurements] 27 | Pos=60,60 28 | Size=375,400 29 | Collapsed=0 30 | 31 | [Window][Menu] 32 | Pos=4,4 33 | Size=481,275 34 | Collapsed=0 35 | 36 | [Window][Statistics] 37 | Pos=1428,6 38 | Size=488,275 39 | Collapsed=0 40 | 41 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/generate-spirv.bat: -------------------------------------------------------------------------------- 1 | glslangvalidator -V visbuffshade.vert -o visbuffshade.vert.spv 2 | glslangvalidator -V visbuffshade.frag -o visbuffshade.frag.spv 3 | glslangvalidator -V visbuffwrite.vert -o visbuffwrite.vert.spv 4 | glslangvalidator -V visbuffwrite.frag -o visbuffwrite.frag.spv 5 | glslangvalidator -V tessshade.vert -o tessshade.vert.spv 6 | glslangvalidator -V tessshade.frag -o tessshade.frag.spv 7 | glslangvalidator -V tesswrite.vert -o tesswrite.vert.spv 8 | glslangvalidator -V tesswrite.tesc -o tesswrite.tesc.spv 9 | glslangvalidator -V tesswrite.tese -o tesswrite.tese.spv 10 | glslangvalidator -V tesswrite.geom -o tesswrite.geom.spv 11 | glslangvalidator -V tesswrite.frag -o tesswrite.frag.spv 12 | glslangvalidator -V ui.vert -o ui.vert.spv 13 | glslangvalidator -V ui.frag -o ui.frag.spv 14 | pause 15 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/VBTTypes.h: -------------------------------------------------------------------------------- 1 | #ifndef VBT_TYPES_H 2 | #define VBT_TYPES_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #pragma region SwapChain 9 | struct SwapChainSupportDetails 10 | { 11 | VkSurfaceCapabilitiesKHR capabilities; 12 | std::vector formats; 13 | std::vector presentModes; 14 | }; 15 | #pragma endregion 16 | 17 | #pragma region Device Queues 18 | struct DeviceQueues 19 | { 20 | VkQueue graphics; 21 | VkQueue present; 22 | }; 23 | 24 | struct QueueFamilyIndices 25 | { 26 | std::optional graphicsFamily; 27 | std::optional presentationFamily; 28 | 29 | bool isSuitable() 30 | { 31 | return graphicsFamily.has_value() && presentationFamily.has_value(); 32 | } 33 | }; 34 | #pragma endregion 35 | 36 | #endif // !VBT_TYPES_H 37 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/visbuffwrite.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | 5 | #define PRIMITIVE_ID_BITS 23 6 | 7 | // Force early depth/stencil test 8 | layout(early_fragment_tests) in; 9 | 10 | // In 11 | layout(location = 0) in flat uint drawID; 12 | 13 | // Out 14 | layout(location = 0) out vec4 outColour; 15 | layout(location = 1) out vec4 visBuff; 16 | 17 | // Engel's packing function (without alpha bit) 18 | uint calculateOutputVBID(uint drawID, uint primitiveID) 19 | { 20 | uint drawID_primID = ((drawID << 23) & 0x7F800000) | (primitiveID & 0x007FFFFF); 21 | return drawID_primID; 22 | } 23 | 24 | void main() 25 | { 26 | // Write to colour attachments to avoid undefined behaviour 27 | outColour = vec4(0.0); 28 | 29 | // Fill visibility buffer 30 | visBuff = unpackUnorm4x8(calculateOutputVBID(drawID, gl_PrimitiveID + 1)); // Offset primitive ID so that the first primitive in each draw call is not lost due to being 0 31 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/visbuffwrite.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | #extension GL_ARB_shader_draw_parameters : enable 5 | 6 | // Constants 7 | const float heightTexScale = 8.0f; 8 | const float heightScale = 5.0f; 9 | 10 | // Descriptors 11 | layout(binding = 0) uniform UniformBufferObject 12 | { 13 | mat4 mvp; 14 | mat4 proj; 15 | } ubo; 16 | layout(binding = 1) uniform sampler2D heightmap; 17 | 18 | // In 19 | layout(location = 0) in vec3 inPosition; 20 | layout(location = 1) in vec3 inNormal; 21 | layout(location = 2) in vec2 inTexCoords; 22 | 23 | // Out 24 | layout(location = 0) flat out uint drawID; 25 | out gl_PerVertex 26 | { 27 | vec4 gl_Position; 28 | }; 29 | 30 | void main() 31 | { 32 | // Displace height 33 | vec3 pos = inPosition; 34 | pos.y += texture(heightmap, inTexCoords / heightTexScale).r * heightScale; 35 | 36 | // Screen Position 37 | vec4 vertScreenPos = ubo.mvp * vec4(pos, 1.0); 38 | gl_Position = vertScreenPos; 39 | 40 | // DrawID 41 | drawID = gl_DrawIDARB; 42 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tesswrite.geom: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | 5 | layout (triangles) in; 6 | layout (triangle_strip, max_vertices = 3) out; 7 | 8 | layout (location = 0) in vec3 inTessCoords[]; 9 | 10 | layout (location = 0) flat out int primitiveID; 11 | layout (location = 1) flat out uvec3 outTessCoords; 12 | 13 | void main(void) 14 | { 15 | // Pack tessellation coordinates into uint vector 16 | uint tessCoord0 = packUnorm4x8(vec4(inTessCoords[0], 0.0)); 17 | uint tessCoord1 = packUnorm4x8(vec4(inTessCoords[1], 0.0)); 18 | uint tessCoord2 = packUnorm4x8(vec4(inTessCoords[2], 0.0)); 19 | outTessCoords = uvec3(tessCoord0, tessCoord1, tessCoord2); 20 | 21 | // Store patch ID 22 | primitiveID = gl_PrimitiveIDIn; 23 | 24 | // Positions are already in screen space from evaluation stage 25 | gl_Position = gl_in[0].gl_Position; 26 | EmitVertex(); 27 | 28 | gl_Position = gl_in[1].gl_Position; 29 | EmitVertex(); 30 | 31 | gl_Position = gl_in[2].gl_Position; 32 | EmitVertex(); 33 | 34 | EndPrimitive(); 35 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/DirectionalLight.h: -------------------------------------------------------------------------------- 1 | #ifndef DIRECTIONALLIGHT_H 2 | #define DIRECTIONALLIGHT_H 3 | 4 | #include 5 | #include "Buffer.h" 6 | 7 | struct DirectionalLightUBO 8 | { 9 | glm::vec4 direction; 10 | glm::vec4 ambient; 11 | glm::vec4 diffuse; 12 | }; 13 | 14 | namespace vbt 15 | { 16 | class DirectionalLight 17 | { 18 | public: 19 | struct InitInfo 20 | { 21 | glm::vec4 direction; 22 | glm::vec4 ambient; 23 | glm::vec4 diffuse; 24 | }; 25 | 26 | void Init(InitInfo info, VmaAllocator& allocator); 27 | void UpdateUBO(VmaAllocator& allocator); 28 | void SetupUBODescriptors(VkDescriptorSet dstSet, uint32_t binding, uint32_t count); 29 | void CleanUp(VmaAllocator& allocator); 30 | vbt::Buffer UBO() { return ubo; } 31 | glm::vec4 Direction() { return direction; } 32 | glm::vec4 Ambient() { return ambient; } 33 | glm::vec4 Diffuse() { return diffuse; } 34 | void SetDirection(glm::vec4 dir) { direction = dir; } 35 | void SetAmbient(glm::vec4 amb) { ambient = amb; } 36 | void SetDiffuse(glm::vec4 diff) { diffuse = diff; } 37 | 38 | private: 39 | glm::vec4 direction; 40 | glm::vec4 ambient; 41 | glm::vec4 diffuse; 42 | 43 | vbt::Buffer ubo; 44 | }; 45 | } 46 | #endif -------------------------------------------------------------------------------- /VisibilityBufferTessellation/PhysicalDevice.h: -------------------------------------------------------------------------------- 1 | #ifndef PHYSICALDEVICE_H 2 | #define PHYSICALDEVICE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "VBTTypes.h" 9 | 10 | #pragma region Required Extensions 11 | const std::vector deviceExtensions 12 | { 13 | VK_KHR_SWAPCHAIN_EXTENSION_NAME, 14 | "VK_KHR_shader_draw_parameters" 15 | }; 16 | #pragma endregion 17 | 18 | namespace vbt 19 | { 20 | class PhysicalDevice 21 | { 22 | public: 23 | void Init(VkInstance instance, VkSurfaceKHR surface); 24 | static QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface); 25 | 26 | VkPhysicalDevice VkHandle() const { return physicalDevice; } 27 | DeviceQueues* Queues() { return &queues; } 28 | const std::vector Extensions() { return deviceExtensions; } 29 | 30 | private: 31 | void SelectPhysicalDevice(VkInstance instance, VkSurfaceKHR surface); 32 | bool isDeviceSuitable(VkPhysicalDevice device, VkSurfaceKHR surface); 33 | bool CheckDeviceExtensionSupport(VkPhysicalDevice device); 34 | SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice device, VkSurfaceKHR surface); 35 | 36 | VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; 37 | DeviceQueues queues; 38 | }; 39 | } 40 | 41 | #endif // !PHYSICALDEVICE_H 42 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tesswrite.tesc: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_ARB_separate_shader_objects : enable 4 | #extension GL_ARB_shading_language_420pack : enable 5 | 6 | layout(binding = 0) uniform UniformBufferObject 7 | { 8 | uint tessellationFactor; 9 | uint showVisibilityBuffer; 10 | uint showTessCoordsBuffer; 11 | uint showInterpolatedTexCoords; 12 | uint wireframe; 13 | } settings; 14 | 15 | layout (vertices = 3) out; 16 | 17 | layout (location = 0) in vec2 inTexCoords[]; 18 | 19 | layout (location = 0) out vec2 outTexCoords[3]; 20 | 21 | void main() 22 | { 23 | if (gl_InvocationID == 0) 24 | { 25 | if (settings.tessellationFactor > 0) 26 | { 27 | gl_TessLevelOuter[0] = settings.tessellationFactor; 28 | gl_TessLevelOuter[1] = settings.tessellationFactor; 29 | gl_TessLevelOuter[2] = settings.tessellationFactor; 30 | gl_TessLevelInner[0] = settings.tessellationFactor; 31 | } 32 | else 33 | { 34 | // Tessellation factor can be set to zero by example 35 | // to demonstrate a simple passthrough 36 | gl_TessLevelInner[0] = 1.0; 37 | gl_TessLevelOuter[0] = 1.0; 38 | gl_TessLevelOuter[1] = 1.0; 39 | gl_TessLevelOuter[2] = 1.0; 40 | } 41 | } 42 | 43 | // Pass world position through 44 | gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; 45 | outTexCoords[gl_InvocationID] = inTexCoords[gl_InvocationID]; 46 | } 47 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef BUFFER_H 2 | #define BUFFER_H 3 | 4 | #include "vk_mem_alloc.h" 5 | 6 | namespace vbt 7 | { 8 | class Buffer 9 | { 10 | public: 11 | void Create(VkDeviceSize size, VkBufferUsageFlags usage, VmaMemoryUsage allocUsage, VkMemoryPropertyFlags properties, VmaAllocator& allocator); 12 | void MapData(void* data, VmaAllocator& allocator); 13 | void Flush(VmaAllocator& allocator, VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0); 14 | void Map(VmaAllocator& allocator); 15 | void Unmap(VmaAllocator& allocator); 16 | void SetupDescriptor(VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0); 17 | void SetupDescriptorWriteSet(VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count); 18 | void CleanUp(VmaAllocator& allocator); 19 | 20 | VkBuffer VkHandle() { return buffer; } 21 | VkDescriptorBufferInfo* DescriptorInfo() { return &descriptor; } 22 | VkWriteDescriptorSet WriteDescriptorSet() const { return descriptorWriteSet; } 23 | void* mappedRange = nullptr; 24 | 25 | protected: 26 | VkBuffer buffer; 27 | VmaAllocation bufferMemory; 28 | VkDeviceSize bufferSize; 29 | 30 | VkBufferUsageFlags usageFlags; 31 | VkMemoryPropertyFlags propertyFlags; 32 | VmaMemoryUsage allocationUsage; 33 | VkDescriptorBufferInfo descriptor; 34 | VkWriteDescriptorSet descriptorWriteSet; 35 | }; 36 | } 37 | 38 | #endif -------------------------------------------------------------------------------- /VisibilityBufferTessellation/DirectionalLight.cpp: -------------------------------------------------------------------------------- 1 | #include "DirectionalLight.h" 2 | 3 | namespace vbt 4 | { 5 | void DirectionalLight::Init(InitInfo info, VmaAllocator& allocator) 6 | { 7 | diffuse = info.diffuse; 8 | direction = info.direction; 9 | ambient = info.ambient; 10 | 11 | // Setup UBO 12 | VkDeviceSize bufferSize = sizeof(DirectionalLightUBO); 13 | DirectionalLightUBO uboData = {}; 14 | uboData.diffuse = diffuse; 15 | uboData.direction = direction; 16 | uboData.ambient = ambient; 17 | 18 | ubo.Create(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, allocator); 19 | ubo.MapData(&uboData, allocator); 20 | } 21 | 22 | void DirectionalLight::CleanUp(VmaAllocator& allocator) 23 | { 24 | ubo.CleanUp(allocator); 25 | } 26 | 27 | void DirectionalLight::SetupUBODescriptors(VkDescriptorSet dstSet, uint32_t binding, uint32_t count) 28 | { 29 | ubo.SetupDescriptor(sizeof(DirectionalLightUBO), 0); 30 | ubo.SetupDescriptorWriteSet(dstSet, binding, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, count); 31 | } 32 | 33 | void DirectionalLight::UpdateUBO(VmaAllocator& allocator) 34 | { 35 | DirectionalLightUBO uboData = {}; 36 | uboData.diffuse = diffuse; 37 | uboData.direction = direction; 38 | uboData.ambient = ambient; 39 | 40 | ubo.MapData(&uboData, allocator); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tesswrite.tese: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #extension GL_ARB_separate_shader_objects : enable 4 | #extension GL_ARB_shading_language_420pack : enable 5 | 6 | // Constants 7 | const float heightTexScale = 8.0f; 8 | const float heightScale = 5.0f; 9 | 10 | // Descriptors 11 | layout(binding = 1) uniform UniformBufferObject 12 | { 13 | mat4 mvp; 14 | mat4 proj; 15 | } ubo; 16 | layout(binding = 2) uniform sampler2D heightmap; 17 | 18 | // In 19 | layout(triangles, equal_spacing, cw) in; 20 | layout(location = 0) in vec2 inTexCoords[]; 21 | 22 | // Out 23 | layout (location = 0) out vec3 outTessCoords; 24 | 25 | vec2 interpolate2D(vec2 v0, vec2 v1, vec2 v2) 26 | { 27 | return vec2(gl_TessCoord.x) * v0 + vec2(gl_TessCoord.y) * v1 + vec2(gl_TessCoord.z) * v2; 28 | } 29 | 30 | vec3 interpolate3D(vec3 v0, vec3 v1, vec3 v2) 31 | { 32 | return vec3(gl_TessCoord.x) * v0 + vec3(gl_TessCoord.y) * v1 + vec3(gl_TessCoord.z) * v2; 33 | } 34 | 35 | void main() 36 | { 37 | // Interpolate positions 38 | vec3 pos = interpolate3D(gl_in[0].gl_Position.xyz, gl_in[1].gl_Position.xyz, gl_in[2].gl_Position.xyz); 39 | 40 | // Displace height 41 | vec2 tex = interpolate2D(inTexCoords[0], inTexCoords[1], inTexCoords[2]); 42 | pos.y += texture(heightmap, tex / heightTexScale).r * heightScale; 43 | 44 | // Perspective projection 45 | gl_Position = ubo.mvp * vec4(pos, 1.0); 46 | outTessCoords = gl_TessCoord; 47 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Terrain.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRAIN_H 2 | #define TERRAIN_H 3 | 4 | #include "Mesh.h" 5 | #include "Texture.h" 6 | #include "Buffer.h" 7 | #include "glm\glm.hpp" 8 | 9 | const std::string TEXTURE_PATH = "textures/sand.jpg"; 10 | const std::string HEIGHTMAP_PATH = "textures/sandheightmap.jpg"; 11 | const std::string NORMALMAP_PATH = "textures/sandnormals.png"; 12 | 13 | namespace vbt 14 | { 15 | class Terrain : public Mesh 16 | { 17 | public: 18 | struct InitInfo 19 | { 20 | int subdivisions = 64; 21 | int width = 32; 22 | float uvScale = 5.0f; 23 | 24 | InitInfo() 25 | {} 26 | }; 27 | 28 | int Init(VmaAllocator& allocator, VkDevice device, PhysicalDevice physDevice, VkCommandPool& cmdPool, InitInfo info); 29 | void SetupTextureDescriptor(VkImageLayout layout, VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count); 30 | void SetupHeightmapDescriptor(VkImageLayout layout, VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count); 31 | void SetupNormalmapDescriptor(VkImageLayout layout, VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count); 32 | void CleanUp(VmaAllocator& allocator, VkDevice device); 33 | 34 | Texture GetTexture() { return texture; } 35 | Texture Heightmap() { return heightmap; } 36 | Texture Normalmap() { return normalmap; } 37 | 38 | private: 39 | int Generate(int verticesPerEdge, int width, float uvScale); 40 | 41 | vbt::Texture texture; 42 | vbt::Texture heightmap; 43 | vbt::Texture normalmap; 44 | }; 45 | } 46 | 47 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vulkan | Visibility Buffer with Tessellation 2 | 3 | The visibility buffer rendering technique is a variant on deferred shading which minimises memory traffic by storing only necessary 4 | primitive information to the g-buffer. 5 | 6 | Whereas a typical deferred g-buffer may be 24-32 bytes, the visibility buffer consists only of primitive ID and draw call ID packed into 7 | a 4-byte per-sample render target in the forward pass. The deferred pass may then access geometry buffers to manually interpolate surface attributes and perform shading as normal. 8 | 9 | This project research implements a typical visibility buffer pipeline alongside a second which implements hardware tessellation. 10 | In doing so, more information must be stored to the visibility buffer, and hence more bandwidth usage, but the net performance gain 11 | by generating detail by tessellation may prove beneficial. 12 | 13 | A bespoke Vulkan framework has been built from the ground up, and the two renderers built up in parallel with as much shared resources as possible. Following the thorough testing of both pipelines, I wrote a full research paper on the project. This can be supplied on request, if you're interested! 14 | 15 | Further resources surrounding the visibility buffer and it's implementation can be found via the following resources: 16 | 17 | Burns and Hunt (2013): http://jcgt.org/published/0002/02/04/ 18 | 19 | Schied and Daschbacher (2015): http://cg.ivd.kit.edu/publications/2015/dais/DAIS.pdf 20 | 21 | Engel (2016, 2018): https://diaryofagraphicsprogrammer.blogspot.com/2018/03/triangle-visibility-buffer.html 22 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/VisibilityBufferTessellation.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2020 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VisibilityBufferTessellation", "VisibilityBufferTessellation.vcxproj", "{B3A9CFF5-C647-4253-AD27-C40BE345D2D8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {B3A9CFF5-C647-4253-AD27-C40BE345D2D8}.Debug|x64.ActiveCfg = Debug|x64 17 | {B3A9CFF5-C647-4253-AD27-C40BE345D2D8}.Debug|x64.Build.0 = Debug|x64 18 | {B3A9CFF5-C647-4253-AD27-C40BE345D2D8}.Debug|x86.ActiveCfg = Debug|Win32 19 | {B3A9CFF5-C647-4253-AD27-C40BE345D2D8}.Debug|x86.Build.0 = Debug|Win32 20 | {B3A9CFF5-C647-4253-AD27-C40BE345D2D8}.Release|x64.ActiveCfg = Release|x64 21 | {B3A9CFF5-C647-4253-AD27-C40BE345D2D8}.Release|x64.Build.0 = Release|x64 22 | {B3A9CFF5-C647-4253-AD27-C40BE345D2D8}.Release|x86.ActiveCfg = Release|Win32 23 | {B3A9CFF5-C647-4253-AD27-C40BE345D2D8}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {365AE8B7-1DAA-453C-A66D-F8BA0C9DB871} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tesswrite.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | 5 | #define PRIMITIVE_ID_BITS 23 6 | 7 | // Force early depth/stencil test 8 | layout(early_fragment_tests) in; 9 | 10 | // In 11 | layout (location = 0) flat in int primitiveID; 12 | layout (location = 1) flat in uvec3 inTessCoords; 13 | // Out 14 | layout(location = 0) out vec4 outColour; 15 | layout(location = 1) out vec4 visBuff; 16 | layout(location = 2) out vec4 tessCoordsBuff1; 17 | layout(location = 3) out vec4 tessCoordsBuff2; 18 | layout(location = 4) out float tessCoordsBuff3; 19 | 20 | // Engel's packing function (without alpha bit) 21 | uint calculateOutputVBID(uint drawID, uint primitiveID) 22 | { 23 | uint drawID_primID = ((drawID << 23) & 0x7F800000) | (primitiveID & 0x007FFFFF); 24 | return drawID_primID; 25 | } 26 | 27 | void main() 28 | { 29 | // Write to colour attachments to avoid undefined behaviour 30 | outColour = vec4(0.0); 31 | 32 | // Fill visibility buffer 33 | visBuff = unpackUnorm4x8(calculateOutputVBID(0, primitiveID + 1)); // Offset primitive ID so that the first primitive in each draw call is not lost due to being 0 34 | 35 | // Recover tess coords from geometry shader 36 | vec3 tessCoord0 = unpackUnorm4x8(inTessCoords.x).xyz; 37 | vec3 tessCoord1 = unpackUnorm4x8(inTessCoords.y).xyz; 38 | vec3 tessCoord2 = unpackUnorm4x8(inTessCoords.z).xyz; 39 | 40 | // Store tess coords in buffers 41 | tessCoordsBuff1 = vec4(tessCoord0, tessCoord1.x); 42 | tessCoordsBuff2 = vec4(tessCoord1.yz, tessCoord2.xy); 43 | tessCoordsBuff3 = tessCoord2.z; 44 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Camera.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERA_H 2 | #define CAMERA_H 3 | 4 | #define GLM_FORCE_RADIANS 5 | #define GLM_FORCE_DEPTH_ZERO_TO_ONE 6 | #include "glm\glm.hpp" 7 | #include "glm\gtc\matrix_transform.hpp" 8 | #include "glm\gtc\quaternion.hpp" 9 | 10 | namespace vbt 11 | { 12 | class Camera 13 | { 14 | public: 15 | void Update(float frameTime); 16 | void SetPerspective(float fov, float aspect, float nearPlane, float farPlane, bool setAsDefault = false); 17 | void SetPosition(glm::vec3 pos, bool setAsDefault = false); 18 | void SetRotation(glm::vec3 rot, bool setAsDefault = false); 19 | void Rotate(glm::vec3 delta); 20 | void Translate(glm::vec3 delta); 21 | void Reset(); 22 | 23 | glm::vec3 Position() { return position; } 24 | glm::vec3 Rotation() { return rotation; } 25 | 26 | glm::mat4 ViewMatrix() const { return viewMatrix; } 27 | glm::mat4 ProjectionMatrix() const { return projMatrix; } 28 | 29 | float rotateSpeed = 0.25f; 30 | float moveSpeed = 2.5f; 31 | 32 | bool updated = false; 33 | 34 | struct 35 | { 36 | bool forward = false; 37 | bool back = false; 38 | bool left = false; 39 | bool right = false; 40 | bool up = false; 41 | bool down = false; 42 | } input; 43 | 44 | private: 45 | void UpdateViewMatrix(); 46 | bool IsMoving(); 47 | 48 | glm::mat4 projMatrix; 49 | glm::mat4 viewMatrix; 50 | 51 | glm::vec3 position = glm::vec3(); 52 | glm::vec3 rotation = glm::vec3(); 53 | float fov; 54 | float nearPlane, farPlane; 55 | 56 | // Used to reset camera to initial position 57 | glm::mat4 defaultProjMatrix; 58 | glm::vec3 defaultPosition = glm::vec3(); 59 | glm::vec3 defaultRotation = glm::vec3(); 60 | }; 61 | } 62 | 63 | #endif -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Image.h: -------------------------------------------------------------------------------- 1 | #ifndef IMAGE_H 2 | #define IMAGE_H 3 | 4 | #include "PhysicalDevice.h" 5 | #include 6 | #include "vk_mem_alloc.h" 7 | 8 | namespace vbt 9 | { 10 | class Image 11 | { 12 | public: 13 | void Create(uint32_t imageWidth, uint32_t imageHeight, VkFormat imageFormat, VkImageTiling tiling, VkImageUsageFlags usage, VmaMemoryUsage memoryUsage, VkMemoryPropertyFlags properties, VmaAllocator& allocator); 14 | void CreateImageView(const VkDevice device, VkImageAspectFlags aspectFlags); 15 | void CreateSampler(VkDevice device, VkSamplerAddressMode addressMode); 16 | void SetUpDescriptorInfo(VkImageLayout layout); 17 | void SetUpDescriptorInfo(VkImageLayout layout, VkSampler sampler); 18 | void SetupDescriptorWriteSet(VkDescriptorSet& dstSet, uint32_t binding, VkDescriptorType type, uint32_t count); 19 | void TransitionLayout(VkImageLayout srcLayout, VkImageLayout dstLayout, VkDevice device, PhysicalDevice physDevice, VkCommandPool& cmdPool); 20 | void CopyFromBuffer(VkBuffer buffer, VkDevice device, PhysicalDevice physDevice, VkCommandPool& cmdPool); 21 | void CleanUp(VmaAllocator& allocator, VkDevice device); 22 | 23 | VkImage VkHandle() { return image; } 24 | VkImageView ImageView() { return imageView; } 25 | VkFormat Format() { return format; } 26 | VkSampler Sampler() { return sampler; } 27 | VkDescriptorImageInfo* DescriptorInfo() { return &descriptor; } 28 | VkWriteDescriptorSet WriteDescriptorSet() const { return writeDescriptorSet; } 29 | 30 | protected: 31 | bool HasStencilComponent(VkFormat format); 32 | 33 | VkImage image; 34 | VkImageLayout imageLayout; 35 | VkImageView imageView; 36 | VkFormat format; 37 | VmaAllocation imageMemory; 38 | VkSampler sampler; 39 | VkDescriptorImageInfo descriptor; 40 | VkWriteDescriptorSet writeDescriptorSet; 41 | 42 | uint32_t width, height = 0; 43 | }; 44 | } 45 | 46 | #endif -------------------------------------------------------------------------------- /VisibilityBufferTessellation/SwapChain.h: -------------------------------------------------------------------------------- 1 | #ifndef SWAPCHAIN_H 2 | #define SWAPCHAIN_H 3 | 4 | #include 5 | #include 6 | #include "VBTTypes.h" 7 | #include "VbtUtils.h" 8 | #include 9 | #include "PhysicalDevice.h" 10 | 11 | namespace vbt 12 | { 13 | class SwapChain 14 | { 15 | public: 16 | void InitSurface(GLFWwindow* window, VkInstance instance); 17 | void InitSwapChain(GLFWwindow* window, VkPhysicalDevice physicalDevice, VkDevice device); 18 | void CleanUpSwapChain(VkDevice device); 19 | void CleanUpSurface(VkInstance instance); 20 | static VkImageView CreateImageView(const VkDevice &device, VkImage &image, VkFormat format, VkImageAspectFlags aspectFlags); 21 | 22 | VkSwapchainKHR VkHandle() const { return swapChain; } 23 | VkSurfaceKHR Surface() const { return surface; } 24 | VkExtent2D Extent() const { return extent; } 25 | VkFormat ImageFormat() const { return imageFormat; } 26 | std::vector Images() const { return images; } 27 | std::vector ImageViews() const { return imageViews; } 28 | 29 | private: 30 | void CreateSwapChain(GLFWwindow* window, VkPhysicalDevice physicalDevice, VkDevice device); 31 | void CreateSurface(GLFWwindow* window, VkInstance instance); 32 | void CreateSwapImageViews(VkDevice device); 33 | SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice device); 34 | VkSurfaceFormatKHR ChooseSurfaceFormat(const std::vector& availableFormats); 35 | VkPresentModeKHR ChoosePresentMode(const std::vector availablePresentModes); 36 | VkExtent2D ChooseExtent(const VkSurfaceCapabilitiesKHR& capabilities, GLFWwindow* window); 37 | 38 | VkSwapchainKHR swapChain; 39 | VkSurfaceKHR surface; // Interface into window system 40 | VkFormat imageFormat; 41 | VkExtent2D extent; 42 | 43 | std::vector images; 44 | std::vector imageViews; 45 | }; 46 | } 47 | 48 | #endif // !SWAPCHAIN_H 49 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Texture.cpp: -------------------------------------------------------------------------------- 1 | #include "Texture.h" 2 | #include "Buffer.h" 3 | #include "VbtUtils.h" 4 | #define STB_IMAGE_IMPLEMENTATION 5 | #include 6 | 7 | namespace vbt 8 | { 9 | void Texture::LoadAndCreate(std::string path, VmaAllocator& allocator, VkDevice device, PhysicalDevice physDevice, VkCommandPool& cmdPool) 10 | { 11 | // Load image file with STB library 12 | int texWidth, texHeight, texChannels; 13 | stbi_uc* pixels = stbi_load(path.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); 14 | VkDeviceSize imageSize = texWidth * texHeight * 4; 15 | if (!pixels) 16 | { 17 | throw std::runtime_error("Failed to load texture image"); 18 | } 19 | 20 | // Create a staging buffer for the pixel data 21 | Buffer stagingBuffer; 22 | stagingBuffer.Create(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, allocator); 23 | 24 | // Copy the pixel values directly to the buffer 25 | stagingBuffer.MapData(pixels, allocator); 26 | 27 | // Clean up pixel memory 28 | stbi_image_free(pixels); 29 | 30 | // Now create Vulkan Image object 31 | Create(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VMA_MEMORY_USAGE_GPU_ONLY, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, allocator); 32 | 33 | // Transition the texture image to the correct layout for recieving the pixel data from the buffer 34 | TransitionLayout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, device, physDevice, cmdPool); 35 | 36 | // Execute the copy from buffer to image 37 | CopyFromBuffer(stagingBuffer.VkHandle(), device, physDevice, cmdPool); 38 | 39 | // Now transition the image layout again to allow for sampling in the shader 40 | TransitionLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, device, physDevice, cmdPool); 41 | 42 | // Free up the staging buffer 43 | stagingBuffer.CleanUp(allocator); 44 | 45 | // Now create the image view and the sampler 46 | CreateImageView(device, VK_IMAGE_ASPECT_COLOR_BIT); 47 | CreateSampler(device, VK_SAMPLER_ADDRESS_MODE_REPEAT); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/VbtImGUI.h: -------------------------------------------------------------------------------- 1 | #ifndef IMGUI_H 2 | #define IMGUI_H 3 | 4 | #include 5 | 6 | #include "vulkan\vulkan.h" 7 | #include "vk_mem_alloc.h" 8 | #include 9 | #include "imgui.h" 10 | #include "imgui_impl_vulkan.h" 11 | #include "imgui_impl_glfw.h" 12 | #include "PhysicalDevice.h" 13 | 14 | #define IMGUI_ENABLED true 15 | 16 | namespace vbt 17 | { 18 | // Forward declarations 19 | class VulkanApplication; 20 | 21 | // ImGui Settings 22 | enum PipelineType { VISIBILITYBUFFER, VB_TESSELLATION }; 23 | struct AppSettings 24 | { 25 | glm::vec3 cameraPos; 26 | glm::vec3 cameraRot; 27 | glm::vec3 lightDirection; 28 | glm::vec4 lightDiffuse; 29 | glm::vec4 lightAmbient; 30 | PipelineType pipeline; 31 | int tessellationFactor = 34; 32 | bool showVisBuff = false; 33 | bool showTessBuff = false; 34 | bool showInterpTex = false; 35 | bool wireframe = false; 36 | bool updateSettings = false; // When true this class will call the UpdateSettings function of the appHandle 37 | }; 38 | 39 | // Renders on-screen GUI via Dear ImGui library 40 | class ImGUI 41 | { 42 | public: 43 | void Init(VulkanApplication* app, GLFWwindow* window, ImGui_ImplVulkan_InitInfo* info, VkRenderPass renderPass, VkCommandPool commandPool, int visBuffTriCount, int tessTriCount); 44 | void Recreate(ImGui_ImplVulkan_InitInfo* info, VkRenderPass renderPass, VkCommandPool commandPool); 45 | void CreateVulkanResources(); 46 | void Update(double frameTime, double forwardTime, double deferredTime, glm::vec3 cameraPos, glm::vec3 cameraRot, glm::vec3 lightDirection, glm::vec4 lightDiffuse, glm::vec4 lightAmbient); 47 | void DrawFrame(VkCommandBuffer commandBuffer); 48 | void CleanUp(); 49 | 50 | private: 51 | void ResetTimeGraphs(); 52 | void SampleFrameTimes(); 53 | void SampleForwardTimes(); 54 | void SampleDeferredTimes(); 55 | 56 | VulkanApplication* appHandle; 57 | 58 | // Vulkan Resources 59 | VkDescriptorPool descriptorPool; 60 | 61 | // Cached values 62 | int visBuffTriCount = 0, tessTricount = 0; 63 | std::array frameTimes{}; 64 | double frameTimeMin = 9999.0, frameTimeMax = 0.0; 65 | double frameTimeSample = 0.0; 66 | std::array forwardTimes{}; 67 | double forwardTimeMin = 9999.0, forwardTimeMax = 0.0; 68 | double forwardTimeSample = 0.0; 69 | std::array deferredTimes{}; 70 | double deferredTimeMin = 9999.0, deferredTimeMax = 0.0; 71 | double deferredTimeSample = 0.0; 72 | }; 73 | } 74 | 75 | #endif -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "Buffer.h" 2 | #include 3 | 4 | namespace vbt 5 | { 6 | void Buffer::Create(VkDeviceSize size, VkBufferUsageFlags usage, VmaMemoryUsage allocUsage, VkMemoryPropertyFlags properties, VmaAllocator& allocator) 7 | { 8 | // Store info 9 | bufferSize = size; 10 | usageFlags = usage; 11 | allocationUsage = allocUsage; 12 | propertyFlags = properties; 13 | 14 | // Fill buffer create info 15 | VkBufferCreateInfo bufferInfo = {}; 16 | bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; 17 | bufferInfo.size = bufferSize; 18 | bufferInfo.usage = usageFlags; 19 | bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 20 | 21 | // Specify memory allocation Info 22 | VmaAllocationCreateInfo allocInfo = {}; 23 | allocInfo.usage = allocationUsage; 24 | allocInfo.requiredFlags = propertyFlags; 25 | 26 | // Create buffer 27 | if (vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &bufferMemory, nullptr) != VK_SUCCESS) 28 | { 29 | throw std::runtime_error("Failed to create vertex buffer"); 30 | } 31 | } 32 | 33 | void Buffer::MapData(void* data, VmaAllocator& allocator) 34 | { 35 | // Map data to staging buffer memory allocation 36 | void* mappedData; 37 | vmaMapMemory(allocator, bufferMemory, &mappedData); 38 | memcpy(mappedData, data, (size_t)bufferSize); 39 | vmaUnmapMemory(allocator, bufferMemory); 40 | } 41 | 42 | void Buffer::Flush(VmaAllocator& allocator, VkDeviceSize size, VkDeviceSize offset) 43 | { 44 | vmaFlushAllocation(allocator, bufferMemory, offset, size); 45 | } 46 | 47 | void Buffer::Map(VmaAllocator& allocator) 48 | { 49 | vmaMapMemory(allocator, bufferMemory, &mappedRange); 50 | } 51 | 52 | void Buffer::Unmap(VmaAllocator& allocator) 53 | { 54 | if (mappedRange) 55 | { 56 | vmaUnmapMemory(allocator, bufferMemory); 57 | mappedRange = nullptr; 58 | } 59 | } 60 | 61 | void Buffer::SetupDescriptor(VkDeviceSize size, VkDeviceSize offset) 62 | { 63 | descriptor.offset = offset; 64 | descriptor.buffer = buffer; 65 | descriptor.range = size; 66 | } 67 | 68 | void Buffer::SetupDescriptorWriteSet(VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count) 69 | { 70 | descriptorWriteSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; 71 | descriptorWriteSet.dstSet = dstSet; 72 | descriptorWriteSet.dstBinding = binding; 73 | descriptorWriteSet.dstArrayElement = 0; 74 | descriptorWriteSet.descriptorType = type; 75 | descriptorWriteSet.descriptorCount = count; 76 | descriptorWriteSet.pBufferInfo = &descriptor; 77 | } 78 | 79 | void Buffer::CleanUp(VmaAllocator& allocator) 80 | { 81 | // Free memory and destroy buffer object 82 | Unmap(allocator); 83 | vmaDestroyBuffer(allocator, buffer, bufferMemory); 84 | } 85 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/VulkanCore.h: -------------------------------------------------------------------------------- 1 | #ifndef VULKANCORE_H 2 | #define VULKANCORE_H 3 | 4 | #define GLFW_INCLUDE_VULKAN 5 | #include 6 | 7 | #include 8 | 9 | #include "VBTTypes.h" 10 | #include "SwapChain.h" 11 | 12 | #pragma region Constants 13 | const int MAX_FRAMES_IN_FLIGHT = 1; 14 | #pragma endregion 15 | 16 | #pragma region Validation Layers 17 | const std::vector validationLayers = 18 | { 19 | "VK_LAYER_LUNARG_standard_validation"/*, 20 | "VK_LAYER_RENDERDOC_Capture"*/ 21 | }; 22 | 23 | #ifdef NDEBUG 24 | const bool enableValidationLayers = false; 25 | #else 26 | const bool enableValidationLayers = true; 27 | #endif 28 | #pragma endregion 29 | 30 | namespace vbt 31 | { 32 | class VulkanCore 33 | { 34 | public: 35 | // Interface Functions 36 | void Init(GLFWwindow* window); 37 | void RecreateSwapchain(GLFWwindow* window); 38 | void CleanUp(); 39 | 40 | // Getters 41 | VkInstance Instance() const { return instance; } 42 | PhysicalDevice PhysDevice() const { return physicalDevice; } 43 | SwapChain Swapchain() const { return swapChain; } 44 | VkDevice Device() const { return device; } 45 | VkDevice* DevicePtr() { return &device; } 46 | std::vector ImageAvailableSemaphores() const { return imageAvailableSemaphores; } 47 | std::vector RenderFinishedSemaphores() const { return renderFinishedSemaphores; } 48 | std::vector Fences() const { return inFlightFences; } 49 | 50 | private: 51 | void CreateInstance(); 52 | void CreateLogicalDevice(); 53 | void CreateSynchronisationObjects(); 54 | 55 | // Debug Functions 56 | void SetupDebugCallback(); 57 | static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); 58 | VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback); 59 | static void DestroyDebutUtilsMessngerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator); 60 | 61 | // Validation Layers and Extensions Functions 62 | bool CheckValidationLayerSupport(); 63 | std::vector GetRequiredInstanceExtensions(); 64 | bool CheckForRequiredGlfwExtensions(const char** glfwExtensions, uint32_t glfwExtensionCount); 65 | 66 | // Core handles 67 | VkInstance instance; 68 | VkDebugUtilsMessengerEXT callback; 69 | SwapChain swapChain; 70 | PhysicalDevice physicalDevice; 71 | VkDevice device; 72 | std::vector imageAvailableSemaphores; 73 | std::vector renderFinishedSemaphores; 74 | std::vector inFlightFences; // Sync objects to prevent CPU submitting too many frames at once 75 | }; 76 | } 77 | 78 | #endif // !VULKANCORE_H 79 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Camera.cpp: -------------------------------------------------------------------------------- 1 | #include "Camera.h" 2 | 3 | namespace vbt 4 | { 5 | void Camera::Update(float frameTime) 6 | { 7 | updated = false; 8 | if (IsMoving()) 9 | { 10 | glm::vec3 forward; 11 | forward.x = -cos(glm::radians(rotation.x)) * sin(glm::radians(rotation.y)); 12 | forward.y = sin(glm::radians(rotation.x)); 13 | forward.z = cos(glm::radians(rotation.x)) * cos(glm::radians(rotation.y)); 14 | forward = glm::normalize(forward); 15 | 16 | float dt = frameTime < 0.001f ? 0.001f : frameTime; 17 | float speed = dt * moveSpeed; 18 | 19 | if (input.forward) 20 | position += forward * speed; 21 | if (input.back) 22 | position -= forward * speed; 23 | if (input.left) 24 | position -= glm::normalize(glm::cross(forward, glm::vec3(0.0f, 1.0f, 0.0f))) * speed; 25 | if (input.right) 26 | position += glm::normalize(glm::cross(forward, glm::vec3(0.0f, 1.0f, 0.0f))) * speed; 27 | if (input.up) 28 | position -= glm::normalize(glm::cross(forward, glm::vec3(1.0f, 0.0f, 0.0f))) * speed; 29 | if (input.down) 30 | position += glm::normalize(glm::cross(forward, glm::vec3(1.0f, 0.0f, 0.0f))) * speed; 31 | 32 | UpdateViewMatrix(); 33 | } 34 | } 35 | 36 | void Camera::UpdateViewMatrix() 37 | { 38 | glm::mat4 rotationMatrix = glm::mat4(1.0f); 39 | glm::mat4 translationMatrix; 40 | 41 | // Perform per-axis rotation 42 | rotationMatrix = glm::rotate(rotationMatrix, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f)); 43 | rotationMatrix = glm::rotate(rotationMatrix, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); 44 | //rotationMatrix = glm::rotate(rotationMatrix, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); // Commented out to lock roll 45 | 46 | // Build translation matrix 47 | translationMatrix = glm::translate(glm::mat4(1.0f), position); 48 | 49 | viewMatrix = rotationMatrix * translationMatrix; 50 | 51 | updated = true; 52 | } 53 | 54 | void Camera::SetPerspective(float fov, float aspect, float nearPlane, float farPlane, bool setAsDefault) 55 | { 56 | this->fov = fov; 57 | this->nearPlane = nearPlane; 58 | this->farPlane = farPlane; 59 | projMatrix = glm::perspective(glm::radians(fov), aspect, nearPlane, farPlane); 60 | if (setAsDefault) defaultProjMatrix = projMatrix; 61 | } 62 | 63 | void Camera::SetPosition(glm::vec3 pos, bool setAsDefault) 64 | { 65 | position = pos; 66 | if (setAsDefault) defaultPosition = position; 67 | UpdateViewMatrix(); 68 | } 69 | 70 | void Camera::SetRotation(glm::vec3 rot, bool setAsDefault) 71 | { 72 | rotation = rot; 73 | if (setAsDefault) defaultRotation = rotation; 74 | UpdateViewMatrix(); 75 | } 76 | 77 | void Camera::Rotate(glm::vec3 delta) 78 | { 79 | rotation += delta; 80 | UpdateViewMatrix(); 81 | } 82 | 83 | void Camera::Translate(glm::vec3 delta) 84 | { 85 | position += delta; 86 | UpdateViewMatrix(); 87 | } 88 | 89 | void Camera::Reset() 90 | { 91 | position = defaultPosition; 92 | rotation = defaultRotation; 93 | projMatrix = defaultProjMatrix; 94 | UpdateViewMatrix(); 95 | } 96 | 97 | bool Camera::IsMoving() 98 | { 99 | return input.forward || input.back || input.left || input.right || input.up || input.down; 100 | } 101 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef MESH_H 2 | #define MESH_H 3 | 4 | #include "Buffer.h" 5 | #include "PhysicalDevice.h" 6 | #include "vk_mem_alloc.h" 7 | #include 8 | #include 9 | #include 10 | 11 | #define GLM_ENABLE_EXPERIMENTAL 12 | #include 13 | 14 | #pragma region Vertex Data 15 | struct Vertex 16 | { 17 | glm::vec3 pos; 18 | glm::vec3 normal; 19 | glm::vec2 uv; 20 | 21 | static VkVertexInputBindingDescription GetBindingDescription() 22 | { 23 | VkVertexInputBindingDescription bindingDescription = {}; 24 | bindingDescription.binding = 0; 25 | bindingDescription.stride = sizeof(Vertex); 26 | bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; 27 | return bindingDescription; 28 | } 29 | 30 | // Create an attribute description PER ATTRIBUTE (currently pos normal and texcoords) 31 | static std::array GetAttributeDescriptions() 32 | { 33 | std::array attributeDescriptions = {}; 34 | attributeDescriptions[0].binding = 0; 35 | attributeDescriptions[0].location = 0; 36 | attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; 37 | attributeDescriptions[0].offset = offsetof(Vertex, pos); 38 | 39 | attributeDescriptions[1].binding = 0; 40 | attributeDescriptions[1].location = 1; 41 | attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; 42 | attributeDescriptions[1].offset = offsetof(Vertex, normal); 43 | 44 | attributeDescriptions[2].binding = 0; 45 | attributeDescriptions[2].location = 2; 46 | attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; 47 | attributeDescriptions[2].offset = offsetof(Vertex, uv); 48 | 49 | return attributeDescriptions; 50 | } 51 | 52 | bool operator==(const Vertex& other) const 53 | { 54 | return pos == other.pos && normal == other.normal && uv == other.uv; 55 | } 56 | }; 57 | 58 | // Hash calculation for Vertex struct 59 | namespace std 60 | { 61 | template<> struct hash 62 | { 63 | size_t operator()(Vertex const& vertex) const 64 | { 65 | return ((hash()(vertex.pos) ^ 66 | (hash()(vertex.normal) << 1)) >> 1) ^ 67 | (hash()(vertex.uv) << 1); 68 | } 69 | }; 70 | } 71 | 72 | // Packed 16-byte aligned vertex attributes for shader binding. 73 | struct VertexAttributes 74 | { 75 | glm::vec4 posXYZnormX; 76 | glm::vec4 normYZtexXY; 77 | }; 78 | #pragma endregion 79 | 80 | namespace vbt 81 | { 82 | class Mesh 83 | { 84 | public: 85 | void LoadFromFile(std::string path); 86 | void SetupIndexBufferDescriptor(VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count); 87 | void SetupAttributeBufferDescriptor(VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count); 88 | void CleanUp(VmaAllocator& allocator); 89 | 90 | Buffer VertexBuffer() const { return vertexBuffer; } 91 | Buffer IndexBuffer() const { return indexBuffer; } 92 | Buffer AttributeBuffer() const { return attributeBuffer; } 93 | std::vector Vertices() const { return vertices; } 94 | std::vector Indices() const { return indices; } 95 | std::vector PackedVertexAttributes() const { return vertexAttributeData; } 96 | 97 | protected: 98 | void CreateBuffers(VmaAllocator& allocator, VkDevice device, PhysicalDevice physDevice, VkCommandPool& cmdPool); 99 | 100 | Buffer vertexBuffer; 101 | Buffer indexBuffer; 102 | Buffer attributeBuffer; 103 | std::vector vertices; 104 | std::vector indices; 105 | std::vector vertexAttributeData; 106 | }; 107 | } 108 | 109 | #endif 110 | 111 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/VbtUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPERFUNCTIONS_H 2 | #define HELPERFUNCTIONS_H 3 | 4 | #include "PhysicalDevice.h" 5 | #include 6 | 7 | namespace vbt 8 | { 9 | #define SCAST_U32(val) static_cast(val) 10 | 11 | static VkCommandBuffer BeginSingleTimeCommands(VkDevice& device, VkCommandPool& cmdPool) 12 | { 13 | // Set up a command buffer to perform the data transfer 14 | VkCommandBufferAllocateInfo allocInfo = {}; 15 | allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; 16 | allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; 17 | allocInfo.commandPool = cmdPool; 18 | allocInfo.commandBufferCount = 1; 19 | 20 | VkCommandBuffer commandBuffer; 21 | vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); 22 | 23 | // Begin recording 24 | VkCommandBufferBeginInfo beginInfo = {}; 25 | beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 26 | beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; 27 | vkBeginCommandBuffer(commandBuffer, &beginInfo); 28 | 29 | return commandBuffer; 30 | } 31 | 32 | static void EndSingleTimeCommands(VkCommandBuffer commandBuffer, VkDevice& device, PhysicalDevice& physDevice, VkCommandPool& cmdPool) 33 | { 34 | vkEndCommandBuffer(commandBuffer); 35 | 36 | // Now execute the command buffer 37 | VkSubmitInfo submitInfo = {}; 38 | submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 39 | submitInfo.commandBufferCount = 1; 40 | submitInfo.pCommandBuffers = &commandBuffer; 41 | 42 | vkQueueSubmit(physDevice.Queues()->graphics, 1, &submitInfo, VK_NULL_HANDLE); 43 | vkQueueWaitIdle(physDevice.Queues()->graphics); // Maybe use a fence here if performing multiple transfers, allowing driver to optimise 44 | 45 | vkFreeCommandBuffers(device, cmdPool, 1, &commandBuffer); 46 | } 47 | 48 | static void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size, VkDevice& device, PhysicalDevice& physDevice, VkCommandPool& cmdPool) 49 | { 50 | VkCommandBuffer commandBuffer = BeginSingleTimeCommands(device, cmdPool); 51 | 52 | // Copy the data 53 | VkBufferCopy copyRegion = {}; 54 | copyRegion.size = size; 55 | vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); 56 | 57 | EndSingleTimeCommands(commandBuffer, device, physDevice, cmdPool); 58 | } 59 | 60 | static std::vector ReadFile(const std::string& filename) 61 | { 62 | std::ifstream file(filename, std::ios::ate | std::ios::binary); 63 | 64 | if (!file.is_open()) 65 | { 66 | throw std::runtime_error("Failed to open file: " + filename); 67 | } 68 | 69 | // Get size of the file and create a buffer 70 | size_t fileSize = (size_t)file.tellg(); 71 | std::vector buffer(fileSize); 72 | 73 | // Read all bytes 74 | file.seekg(0); 75 | file.read(buffer.data(), fileSize); 76 | file.close(); 77 | 78 | return buffer; 79 | } 80 | 81 | static void ImGuiCheckVKResult(VkResult err) 82 | { 83 | if (err == 0) return; 84 | printf("VkResult %d\n", err); 85 | if (err < 0) 86 | abort(); 87 | } 88 | 89 | // Wraps given angle to the range of [0, 360) 90 | static float WrapAngle(float x) 91 | { 92 | x = fmod(x, 360); 93 | if (x < 0) 94 | x += 360; 95 | return x; 96 | } 97 | 98 | // Calculates number of triangles produced by barycentric subdivision (i.e. tessellation) 99 | // of a single trianlge where outer and inner lod is always equal 100 | static int CalculateTriangleSubdivision(int lod) 101 | { 102 | if (lod < 0) return 1; 103 | if (lod == 0) return 0; 104 | return ((2 * lod - 2) * 3) + CalculateTriangleSubdivision(lod - 2); 105 | } 106 | } 107 | 108 | #endif // !HELPERFUNCTIONS_H 109 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Terrain.cpp: -------------------------------------------------------------------------------- 1 | #include "Terrain.h" 2 | #include "VbtUtils.h" 3 | 4 | namespace vbt 5 | { 6 | // Generates terrain mesh, loads textures and returns triangle count. 7 | int Terrain::Init(VmaAllocator& allocator, VkDevice device, PhysicalDevice physDevice, VkCommandPool& cmdPool, InitInfo info) 8 | { 9 | texture.LoadAndCreate(TEXTURE_PATH, allocator, device, physDevice, cmdPool); 10 | heightmap.LoadAndCreate(HEIGHTMAP_PATH, allocator, device, physDevice, cmdPool); 11 | normalmap.LoadAndCreate(NORMALMAP_PATH, allocator, device, physDevice, cmdPool); 12 | 13 | int triangleCount = Generate(info.subdivisions, info.width, info.uvScale); 14 | 15 | CreateBuffers(allocator, device, physDevice, cmdPool); 16 | 17 | return triangleCount; 18 | } 19 | 20 | void Terrain::SetupTextureDescriptor(VkImageLayout layout, VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count) 21 | { 22 | texture.SetUpDescriptorInfo(layout); 23 | texture.SetupDescriptorWriteSet(dstSet, binding, type, count); 24 | } 25 | 26 | void Terrain::SetupHeightmapDescriptor(VkImageLayout layout, VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count) 27 | { 28 | heightmap.SetUpDescriptorInfo(layout); 29 | heightmap.SetupDescriptorWriteSet(dstSet, binding, type, count); 30 | } 31 | 32 | void Terrain::SetupNormalmapDescriptor(VkImageLayout layout, VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count) 33 | { 34 | normalmap.SetUpDescriptorInfo(layout); 35 | normalmap.SetupDescriptorWriteSet(dstSet, binding, type, count); 36 | } 37 | 38 | void Terrain::CleanUp(VmaAllocator& allocator, VkDevice device) 39 | { 40 | this->Mesh::CleanUp(allocator); 41 | texture.CleanUp(allocator, device); 42 | heightmap.CleanUp(allocator, device); 43 | normalmap.CleanUp(allocator, device); 44 | } 45 | 46 | int Terrain::Generate(int verticesPerEdge, int width, float uvScale) 47 | { 48 | // Get triangle count 49 | const uint32_t quadsPerSide = verticesPerEdge - 1; 50 | int quadCount = quadsPerSide * quadsPerSide; 51 | int triangleCount = quadCount * 2; 52 | 53 | // Get offset from width 54 | float vertexOffset = (float)width / (float)(verticesPerEdge - 1); 55 | 56 | // Generate vertices 57 | for (auto x = 0; x < verticesPerEdge; x++) 58 | { 59 | for (auto z = 0; z < verticesPerEdge; z++) 60 | { 61 | Vertex vertex; 62 | VertexAttributes attributes; 63 | vertex.pos.x = x * vertexOffset + vertexOffset / 2.0f - (float)verticesPerEdge * vertexOffset / 2.0f; 64 | vertex.pos.y = 0; 65 | vertex.pos.z = z * vertexOffset + vertexOffset / 2.0f - (float)verticesPerEdge * vertexOffset / 2.0f; 66 | vertex.uv = glm::vec2((float)x / verticesPerEdge, (float)z / verticesPerEdge) * -uvScale; 67 | vertex.normal = glm::vec3(0.0f, 0.0f, 1.0f); 68 | attributes.posXYZnormX = { vertex.pos.x, vertex.pos.y, vertex.pos.z, vertex.normal.x }; 69 | attributes.normYZtexXY = { vertex.normal.y, vertex.normal.z, vertex.uv.x, vertex.uv.y }; 70 | vertices.push_back(vertex); 71 | vertexAttributeData.push_back(attributes); 72 | } 73 | } 74 | 75 | // Generate triangle list indices 76 | indices.resize(quadCount * 6); 77 | for (auto x = 0; x < quadsPerSide; x++) 78 | { 79 | for (auto y = 0; y < quadsPerSide; y++) 80 | { 81 | uint32_t index = (x + y * quadsPerSide) * 6; 82 | indices[index] = (x + y * verticesPerEdge); // bottom left 83 | indices[index + 1] = indices[index] + verticesPerEdge; // bottom right 84 | indices[index + 2] = indices[index + 1] + 1; // top right 85 | indices[index + 3] = indices[index] + 1; // top left 86 | indices[index + 4] = (x + y * verticesPerEdge); // bottom left 87 | indices[index + 5] = indices[index + 1] + 1; // top right 88 | } 89 | } 90 | 91 | return triangleCount; 92 | } 93 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "Mesh.h" 2 | #include "VbtUtils.h" 3 | 4 | #define TINYOBJLOADER_IMPLEMENTATION 5 | #include 6 | 7 | namespace vbt 8 | { 9 | void Mesh::LoadFromFile(std::string path) 10 | { 11 | tinyobj::attrib_t attribute; 12 | std::vector shapes; 13 | std::vector materials; 14 | std::string err; 15 | 16 | // LoadObj automatically triangulates faces by default, so we don't need to worry about that 17 | if (!tinyobj::LoadObj(&attribute, &shapes, &materials, &err, path.c_str())) 18 | { 19 | throw std::runtime_error(err); 20 | } 21 | 22 | // Use a map to track the index of unique vertices 23 | std::unordered_map uniqueVertices = {}; 24 | 25 | // Combine all shapes into one model 26 | for (const auto& shape : shapes) 27 | { 28 | for (const auto& index : shape.mesh.indices) 29 | { 30 | Vertex vertex = {}; 31 | VertexAttributes vertexAttributes = {}; // For storing geometry attributes only to be accessed by shading pass. Position, normal and tex are packed into 2 vec4s 32 | 33 | // Vertices array is an array of floats, so we have to multiply the index by three each time, and offset by 0/1/2 to get the X/Y/Z components 34 | vertex.pos = 35 | { 36 | attribute.vertices[3 * index.vertex_index + 0], 37 | attribute.vertices[3 * index.vertex_index + 1], 38 | attribute.vertices[3 * index.vertex_index + 2] 39 | }; 40 | vertexAttributes.posXYZnormX.x = vertex.pos.x; 41 | vertexAttributes.posXYZnormX.y = vertex.pos.y; 42 | vertexAttributes.posXYZnormX.z = vertex.pos.z; 43 | 44 | vertex.normal = { 1.0f, 1.0f, 1.0f }; 45 | vertexAttributes.posXYZnormX.w = vertex.normal.x; 46 | vertexAttributes.normYZtexXY.x = vertex.normal.y; 47 | vertexAttributes.normYZtexXY.y = vertex.normal.z; 48 | 49 | vertex.uv = 50 | { 51 | attribute.texcoords[2 * index.texcoord_index + 0], 52 | 1.0f - attribute.texcoords[2 * index.texcoord_index + 1] // Flip texture Y coordinate to match vulkan coord system 53 | }; 54 | vertexAttributes.normYZtexXY.z = vertex.uv.x; 55 | vertexAttributes.normYZtexXY.w = vertex.uv.y; 56 | 57 | // Check if we've already seen this vertex before, and add it to the vertex buffer if not 58 | if (uniqueVertices.count(vertex) == 0) 59 | { 60 | uniqueVertices[vertex] = SCAST_U32(vertices.size()); 61 | vertices.push_back(vertex); 62 | vertexAttributeData.push_back(vertexAttributes); 63 | } 64 | indices.push_back(uniqueVertices[vertex]); 65 | } 66 | } 67 | } 68 | 69 | void Mesh::SetupIndexBufferDescriptor(VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count) 70 | { 71 | indexBuffer.SetupDescriptor(sizeof(indices[0]) * indices.size(), 0); 72 | indexBuffer.SetupDescriptorWriteSet(dstSet, binding, type, count); 73 | } 74 | 75 | void Mesh::SetupAttributeBufferDescriptor(VkDescriptorSet dstSet, uint32_t binding, VkDescriptorType type, uint32_t count) 76 | { 77 | attributeBuffer.SetupDescriptor(sizeof(vertexAttributeData[0]) * vertexAttributeData.size(), 0); 78 | attributeBuffer.SetupDescriptorWriteSet(dstSet, binding, type, count); 79 | } 80 | 81 | void Mesh::CleanUp(VmaAllocator& allocator) 82 | { 83 | vertexBuffer.CleanUp(allocator); 84 | indexBuffer.CleanUp(allocator); 85 | attributeBuffer.CleanUp(allocator); 86 | } 87 | 88 | void Mesh::CreateBuffers(VmaAllocator& allocator, VkDevice device, PhysicalDevice physDevice, VkCommandPool& cmdPool) 89 | { 90 | // Vertex Buffer 91 | VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); 92 | Buffer stagingBuffer; 93 | stagingBuffer.Create(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, allocator); 94 | 95 | // Map vertex data to staging buffer memory allocation 96 | stagingBuffer.MapData(vertices.data(), allocator); 97 | 98 | // Create vertex buffer on device local memory 99 | vertexBuffer.Create(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, allocator); 100 | 101 | // Copy data to new vertex buffer 102 | CopyBuffer(stagingBuffer.VkHandle(), vertexBuffer.VkHandle(), bufferSize, device, physDevice, cmdPool); 103 | 104 | stagingBuffer.CleanUp(allocator); 105 | 106 | // Index Buffer 107 | bufferSize = sizeof(indices[0]) * indices.size(); 108 | stagingBuffer.Create(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, allocator); 109 | 110 | stagingBuffer.MapData(indices.data(), allocator); 111 | 112 | indexBuffer.Create(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, allocator); 113 | 114 | CopyBuffer(stagingBuffer.VkHandle(), indexBuffer.VkHandle(), bufferSize, device, physDevice, cmdPool); 115 | 116 | stagingBuffer.CleanUp(allocator); 117 | 118 | // Attribute Buffer 119 | bufferSize = sizeof(vertexAttributeData[0]) * vertexAttributeData.size(); 120 | stagingBuffer.Create(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, allocator); 121 | 122 | stagingBuffer.MapData(vertexAttributeData.data(), allocator); 123 | 124 | attributeBuffer.Create(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, allocator); 125 | 126 | CopyBuffer(stagingBuffer.VkHandle(), attributeBuffer.VkHandle(), bufferSize, device, physDevice, cmdPool); 127 | 128 | // Clean up staging buffer 129 | stagingBuffer.CleanUp(allocator); 130 | } 131 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/PhysicalDevice.cpp: -------------------------------------------------------------------------------- 1 | #include "PhysicalDevice.h" 2 | 3 | namespace vbt 4 | { 5 | void PhysicalDevice::Init(VkInstance instance, VkSurfaceKHR surface) 6 | { 7 | SelectPhysicalDevice(instance, surface); 8 | } 9 | 10 | void PhysicalDevice::SelectPhysicalDevice(VkInstance instance, VkSurfaceKHR surface) 11 | { 12 | // How many devices are discoverable? 13 | uint32_t deviceCount = 0; 14 | vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); 15 | if (deviceCount == 0) 16 | { 17 | // Uh oh 18 | throw std::runtime_error("Failed to find GPUs with Vulkan Support"); 19 | } 20 | 21 | // Get available devices 22 | std::vector devices(deviceCount); 23 | vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); 24 | 25 | // Are any of the devices suitable for our needs? 26 | for (const auto& device : devices) { 27 | if (isDeviceSuitable(device, surface)) { 28 | physicalDevice = device; 29 | break; 30 | } 31 | } 32 | if (physicalDevice == VK_NULL_HANDLE) { 33 | throw std::runtime_error("Failed to find a suitable GPU"); 34 | } 35 | } 36 | 37 | bool PhysicalDevice::isDeviceSuitable(VkPhysicalDevice device, VkSurfaceKHR surface) 38 | { 39 | // Get properties and features of graphics device 40 | VkPhysicalDeviceProperties deviceProperties; 41 | vkGetPhysicalDeviceProperties(device, &deviceProperties); 42 | // Check for device features 43 | VkPhysicalDeviceFeatures supportedFeatures; 44 | vkGetPhysicalDeviceFeatures(device, &supportedFeatures); 45 | 46 | // We only want dedicated graphics cards 47 | bool discrete = (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU); 48 | float timestampPeriod = deviceProperties.limits.timestampPeriod; 49 | 50 | // Check for required queue families 51 | QueueFamilyIndices indices = FindQueueFamilies(device, surface); 52 | // Check for extension support 53 | bool extensionsSupported = CheckDeviceExtensionSupport(device); 54 | // Check for an adequate swap chain support 55 | bool swapChainAdequate = false; 56 | if (extensionsSupported) 57 | { 58 | SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(device, surface); 59 | swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); 60 | } 61 | 62 | return indices.isSuitable() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy && discrete && supportedFeatures.geometryShader && supportedFeatures.fragmentStoresAndAtomics && supportedFeatures.tessellationShader; 63 | } 64 | 65 | bool PhysicalDevice::CheckDeviceExtensionSupport(VkPhysicalDevice device) 66 | { 67 | // Get all supported device extensions 68 | uint32_t supportedExtensionCount = 0; 69 | vkEnumerateDeviceExtensionProperties(device, nullptr, &supportedExtensionCount, nullptr); 70 | std::vector supportedExtensions(supportedExtensionCount); 71 | vkEnumerateDeviceExtensionProperties(device, nullptr, &supportedExtensionCount, supportedExtensions.data()); 72 | 73 | // Check for the ones we want 74 | std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); 75 | for (const auto& extension : supportedExtensions) 76 | { 77 | // If the supported extension name is found in the required set, erase it 78 | requiredExtensions.erase(extension.extensionName); 79 | } 80 | 81 | // If requiredExtensions is empty then all of them are supported 82 | return requiredExtensions.empty(); 83 | } 84 | 85 | 86 | QueueFamilyIndices PhysicalDevice::FindQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface) 87 | { 88 | QueueFamilyIndices indices; 89 | 90 | // Get supported queue families from physical device 91 | uint32_t queueFamilyCount = 0; 92 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 93 | std::vector queueFamilyProperties(queueFamilyCount); 94 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilyProperties.data()); 95 | 96 | // Check each family for required support. Note: it's likely that graphics and presentation are supported 97 | // by the same queue family for most graphics devices. But just in case, check for each as seperate families. 98 | // Could add some logic to prefer a device that supports both in one family for better performance. 99 | int i = 0; 100 | for (const auto& queueFamily : queueFamilyProperties) 101 | { 102 | // Check for graphics support 103 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) 104 | { 105 | indices.graphicsFamily = i; 106 | } 107 | // Check for presentation support 108 | VkBool32 presentSupport = false; 109 | vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); 110 | if (queueFamilyCount > 0 && presentSupport) 111 | { 112 | indices.presentationFamily = i; 113 | } 114 | 115 | if (indices.isSuitable()) 116 | { 117 | break; 118 | } 119 | 120 | i++; 121 | } 122 | 123 | return indices; 124 | } 125 | 126 | SwapChainSupportDetails PhysicalDevice::QuerySwapChainSupport(VkPhysicalDevice device, VkSurfaceKHR surface) 127 | { 128 | SwapChainSupportDetails details; 129 | 130 | // Get basic surface capabilities 131 | vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); 132 | 133 | // Get supported surface formats 134 | uint32_t supportedFormatCount = 0; 135 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &supportedFormatCount, nullptr); 136 | if (supportedFormatCount > 0) 137 | { 138 | details.formats.resize(supportedFormatCount); 139 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &supportedFormatCount, details.formats.data()); 140 | } 141 | 142 | // Get supported presentation modes 143 | uint32_t supportedPresentModes = 0; 144 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &supportedPresentModes, nullptr); 145 | if (supportedPresentModes > 0) 146 | { 147 | details.presentModes.resize(supportedPresentModes); 148 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &supportedPresentModes, details.presentModes.data()); 149 | } 150 | 151 | return details; 152 | } 153 | 154 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.objs 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/VulkanApplication.h: -------------------------------------------------------------------------------- 1 | #ifndef VULKAN_APPLICATION_H 2 | #define VULKAN_APPLICATION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "vk_mem_alloc.h" 8 | #include "VulkanCore.h" 9 | #include "Buffer.h" 10 | #include "Terrain.h" 11 | #include "Texture.h" 12 | #include "Camera.h" 13 | #include "VbtImGUI.h" 14 | #include "DirectionalLight.h" 15 | 16 | #define GLM_FORCE_RADIANS 17 | #define GLM_FORCE_DEPTH_ZERO_TO_ONE // Ensure that GLM works in Vulkan's clip coordinates of 0.0 to 1.0 18 | #include 19 | #include 20 | 21 | #pragma region Constants 22 | const int WIDTH = 1920; 23 | const int HEIGHT = 1080; 24 | #pragma endregion 25 | 26 | #pragma region Frame Buffers 27 | struct VisibilityBuffer 28 | { 29 | vbt::Image visibility; 30 | }; 31 | 32 | struct TessellationVisibilityBuffer 33 | { 34 | vbt::Image visibility, tessCoords_v1XYZ_v2X, tessCoords_v2YZ_v3XY, tessCoords_v3Z; 35 | }; 36 | #pragma endregion 37 | 38 | #pragma region Uniform Buffers 39 | struct MVPUniformBufferObject 40 | { 41 | glm::mat4 mvp; 42 | glm::mat4 proj; 43 | }; 44 | 45 | struct SettingsUBO 46 | { 47 | uint32_t tessellationFactor = 34; 48 | uint32_t showVisibilityBuffer = 0; 49 | uint32_t showTessCoordsBuffer = 0; 50 | uint32_t showInterpolatedTex = 0; 51 | uint32_t wireframe = 0; 52 | }; 53 | #pragma endregion 54 | 55 | namespace vbt 56 | { 57 | class VulkanApplication { 58 | public: 59 | void Run() 60 | { 61 | InitWindow(); 62 | Init(); 63 | Update(); 64 | CleanUp(); 65 | } 66 | 67 | #if IMGUI_ENABLED 68 | void ApplySettings(AppSettings settings); 69 | #endif 70 | VulkanCore* GetVulkanCore() { return vulkan; } 71 | void SwitchPipeline(PipelineType type); 72 | 73 | const std::string title = "Visibility Buffer Tessellation"; 74 | private: 75 | // Functions ============================================== 76 | #pragma region Core Functions 77 | void InitWindow(); 78 | void Init(); 79 | void Update(); 80 | void CleanUp(); 81 | #pragma endregion 82 | 83 | #if IMGUI_ENABLED 84 | #pragma region ImGui Functions 85 | void InitImGui(VkRenderPass renderPass); 86 | void RecreateImGui(VkRenderPass renderPass); 87 | #pragma endregion 88 | #endif 89 | 90 | #pragma region Geometry Functions 91 | void InitialiseTerrains(); 92 | #pragma endregion 93 | 94 | #pragma region Testing Functions 95 | void CreateTimestampPool(); 96 | void GetTimestampResults(); 97 | #pragma endregion 98 | 99 | #pragma region Input Functions 100 | static void ProcessKeyInput(GLFWwindow* window, int key, int scancode, int action, int mods); 101 | static void ProcessMouseInput(GLFWwindow* window, int button, int action, int mods); 102 | void UpdateMouse(); 103 | #pragma endregion 104 | 105 | #pragma region Presentation and Swap Chain Functions 106 | void RecreateSwapChain(); 107 | void CleanUpSwapChainResources(); 108 | #pragma endregion 109 | 110 | #pragma region Graphics Pipeline Functions 111 | void CreatePipelineCache(); 112 | void CreatePipelineLayouts(); 113 | void CreateShadePipelines(); 114 | void CreateWritePipelines(); 115 | void CreateRenderPasses(); 116 | VkShaderModule CreateShaderModule(const std::vector& code); 117 | #pragma endregion 118 | 119 | #pragma region Drawing Functions 120 | void InitCamera(); 121 | void InitLight(); 122 | void CreateFrameBuffers(); 123 | void CreateFrameBufferAttachment(VkFormat format, VkImageUsageFlags usage, Image* attachment, VmaAllocator& allocator); 124 | void DrawFrame(); 125 | #pragma endregion 126 | 127 | #pragma region Command Buffer Functions 128 | void CreateCommandPool(); 129 | void AllocateCommandBuffers(); 130 | void RecordCommandBuffers(); 131 | #pragma endregion 132 | 133 | #pragma region Depth Buffer Functions 134 | void CreateDepthResources(); 135 | VkFormat FindDepthFormat(); 136 | VkFormat FindSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features); 137 | #pragma endregion 138 | 139 | #pragma region Buffer Functions 140 | void CreateUniformBuffers(); 141 | void UpdateUniformBuffers(); 142 | void CreateVmaAllocator(); 143 | #pragma endregion 144 | 145 | #pragma region Descriptor Functions 146 | void CreateDescriptorPool(); 147 | void CreateShadePassDescriptorSetLayouts(); 148 | void CreateShadePassDescriptorSets(); 149 | void CreateVisBuffWritePassDescriptorSetLayout(); 150 | void CreateWritePassDescriptorSet(); 151 | void CreateTessWritePassDescriptorSetLayout(); 152 | void CreateTessWritePassDescriptorSet(); 153 | #pragma endregion 154 | 155 | #pragma region Other Functions 156 | static void FrameBufferResizeCallback(GLFWwindow* window, int width, int height); 157 | #pragma endregion 158 | 159 | // ========================================================= 160 | 161 | #pragma region Shared Objects 162 | GLFWwindow* window; 163 | VulkanCore* vulkan; 164 | #if IMGUI_ENABLED 165 | ImGUI imGui; 166 | #endif 167 | Camera camera; 168 | DirectionalLight light; 169 | VkPipelineCache pipelineCache; 170 | VkCommandPool commandPool; 171 | VkDescriptorPool descriptorPool; 172 | VkQueryPool timestampPool; 173 | VmaAllocator allocator; 174 | std::vector commandBuffers; 175 | vbt::Image depthImage; 176 | Buffer settingsBuffer; 177 | #pragma endregion 178 | 179 | #pragma region Visibility Buffer Pipeline 180 | VisibilityBuffer visibilityBuffer; 181 | VkRenderPass visBuffRenderPass; 182 | VkPipeline visBuffShadePipeline; 183 | VkPipeline visBuffWritePipeline; 184 | VkPipelineLayout visBuffShadePipelineLayout; 185 | VkPipelineLayout visBuffWritePipelineLayout; 186 | std::vector visBuffFramebuffers; 187 | VkDescriptorSet visBuffWritePassDescSet; 188 | VkDescriptorSetLayout visBuffWritePassDescSetLayout; 189 | std::vector visBuffShadePassDescSets; 190 | VkDescriptorSetLayout visBuffShadePassDescSetLayout; 191 | #pragma endregion 192 | 193 | #pragma region Visibility Buffer + Tessellation Pipeline 194 | TessellationVisibilityBuffer tessVisibilityBuffer; 195 | VkRenderPass tessRenderPass; 196 | VkPipeline tessShadePipeline; 197 | VkPipeline tessWritePipeline; 198 | VkPipelineLayout tessShadePipelineLayout; 199 | VkPipelineLayout tessWritePipelineLayout; 200 | std::vector tessFramebuffers; 201 | VkDescriptorSet tessWritePassDescSet; 202 | VkDescriptorSetLayout tessWritePassDescSetLayout; 203 | std::vector tessShadePassDescSets; 204 | VkDescriptorSetLayout tessShadePassDescSetLayout; 205 | #pragma endregion 206 | 207 | #pragma region Geometry 208 | // Two terrains, one detailed, one coarse for tessellation. 209 | Terrain visBuffTerrain; 210 | Terrain tessTerrain; 211 | Buffer mvpUniformBuffer; 212 | #pragma endregion 213 | 214 | #pragma region Input, Settings, Counters and Flags 215 | PipelineType currentPipeline = VISIBILITYBUFFER; 216 | SettingsUBO renderSettingsUbo; 217 | size_t currentFrame = 0; 218 | bool framebufferResized = false; 219 | double frameTime = 0.0; 220 | double forwardPassTime = 0.0; 221 | double deferredPassTime = 0.0; 222 | glm::vec2 mousePosition = glm::vec3(); 223 | bool mouseLeftDown = false; 224 | bool mouseRightDown = false; 225 | int visBuffTerrainTriCount = 0; 226 | int tessTerrainTriCount = 0; 227 | #pragma endregion 228 | }; 229 | } 230 | #endif -------------------------------------------------------------------------------- /VisibilityBufferTessellation/VisibilityBufferTessellation.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {f9f40e4b-49f2-4cee-a65c-33f2f1c3c0d9} 18 | 19 | 20 | {81f1b356-5bb0-4a99-9efe-bd24778852ad} 21 | 22 | 23 | {4edbe599-34b5-4c34-9fc6-312169527645} 24 | 25 | 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files 53 | 54 | 55 | Source Files 56 | 57 | 58 | Source Files 59 | 60 | 61 | Source Files\ImGui 62 | 63 | 64 | Source Files\ImGui 65 | 66 | 67 | Source Files\ImGui 68 | 69 | 70 | Source Files\ImGui 71 | 72 | 73 | Source Files 74 | 75 | 76 | Source Files\ImGui 77 | 78 | 79 | Source Files\ImGui 80 | 81 | 82 | Source Files 83 | 84 | 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | Header Files 94 | 95 | 96 | Header Files 97 | 98 | 99 | Header Files 100 | 101 | 102 | Header Files 103 | 104 | 105 | Header Files 106 | 107 | 108 | Header Files 109 | 110 | 111 | Header Files 112 | 113 | 114 | Header Files 115 | 116 | 117 | Header Files 118 | 119 | 120 | Header Files 121 | 122 | 123 | Header Files 124 | 125 | 126 | Header Files\ImGui 127 | 128 | 129 | Header Files\ImGui 130 | 131 | 132 | Header Files\ImGui 133 | 134 | 135 | Header Files\ImGui 136 | 137 | 138 | Header Files\ImGui 139 | 140 | 141 | Header Files\ImGui 142 | 143 | 144 | Header Files 145 | 146 | 147 | Header Files\ImGui 148 | 149 | 150 | Header Files\ImGui 151 | 152 | 153 | Header Files 154 | 155 | 156 | 157 | 158 | Shaders 159 | 160 | 161 | Shaders 162 | 163 | 164 | Shaders 165 | 166 | 167 | Shaders 168 | 169 | 170 | Shaders 171 | 172 | 173 | Shaders 174 | 175 | 176 | Shaders 177 | 178 | 179 | Shaders 180 | 181 | 182 | Shaders 183 | 184 | 185 | Shaders 186 | 187 | 188 | Shaders 189 | 190 | 191 | Shaders 192 | 193 | 194 | Shaders 195 | 196 | 197 | -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/visbuffshade.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | 5 | // Structs 6 | struct Vertex 7 | { 8 | vec4 posXYZnormX; 9 | vec4 normYZtexXY; 10 | }; 11 | struct Index 12 | { 13 | uint val; 14 | }; 15 | struct DerivativesOutput 16 | { 17 | vec3 dbDx; 18 | vec3 dbDy; 19 | }; 20 | 21 | // Constants 22 | const float heightTexScale = 8.0f; 23 | const float heightScale = 5.0f; 24 | 25 | // In 26 | layout(location = 0) in vec2 inScreenPos; 27 | 28 | // Out 29 | layout(location = 0) out vec4 outColour; 30 | 31 | // Descriptors 32 | layout (set = 0, binding = 0) uniform sampler2D textureSampler; 33 | layout (input_attachment_index = 0, set = 0, binding = 1) uniform subpassInput inputVisibility; 34 | layout(set = 0, binding = 2) uniform UniformBufferObject 35 | { 36 | mat4 mvp; 37 | mat4 proj; 38 | } ubo; 39 | layout (std430, set = 0, binding = 3) readonly buffer IndxBuff 40 | { 41 | Index indexBuffer[]; 42 | }; 43 | layout (std430, set = 0, binding = 4) readonly buffer VertBuff 44 | { 45 | Vertex vertexBuffer[]; 46 | }; 47 | layout(set = 0, binding = 5) uniform SettingsUniformBufferObject 48 | { 49 | uint tessellationFactor; 50 | uint showVisibilityBuffer; 51 | uint showTessCoordsBuffer; 52 | uint showInterpolatedTexCoords; 53 | uint wireframe; 54 | } settings; 55 | layout(set = 0, binding = 6) uniform sampler2D heightmap; 56 | layout(set = 0, binding = 7) uniform sampler2D normalmap; 57 | layout(set = 0, binding = 8) uniform DirectionalLightUniformBufferObject 58 | { 59 | vec4 direction; 60 | vec4 ambient; 61 | vec4 diffuse; 62 | } light; 63 | 64 | // Interpolate 2D attributes using the partial derivatives and generates dx and dy for texture sampling. 65 | vec2 Interpolate2DAttributes(mat3x2 attributes, vec3 dbDx, vec3 dbDy, vec2 d) 66 | { 67 | vec3 attr0 = vec3(attributes[0].x, attributes[1].x, attributes[2].x); 68 | vec3 attr1 = vec3(attributes[0].y, attributes[1].y, attributes[2].y); 69 | vec2 attribute_x = vec2(dot(dbDx,attr0), dot(dbDx,attr1)); 70 | vec2 attribute_y = vec2(dot(dbDy,attr0), dot(dbDy,attr1)); 71 | vec2 attribute_s = attributes[0]; 72 | 73 | vec2 result = (attribute_s + d.x * attribute_x + d.y * attribute_y); 74 | return result; 75 | } 76 | 77 | // Interpolate vertex attributes at point 'd' using the partial derivatives 78 | vec3 Interpolate3DAttributes(mat3 attributes, vec3 dbDx, vec3 dbDy, vec2 d) 79 | { 80 | vec3 attribute_x = attributes * dbDx; 81 | vec3 attribute_y = attributes * dbDy; 82 | vec3 attribute_s = attributes[0]; 83 | 84 | return (attribute_s + d.x * attribute_x + d.y * attribute_y); 85 | } 86 | 87 | // Engel's barycentric coord partial derivs function. Follows equation from [Schied][Dachsbacher] 88 | // Computes the partial derivatives of point's barycentric coordinates from the projected screen space vertices 89 | DerivativesOutput ComputePartialDerivatives(vec2 v[3]) 90 | { 91 | DerivativesOutput derivatives; 92 | float d = 1.0 / determinant(mat2(v[2] - v[1], v[0] - v[1])); 93 | derivatives.dbDx = vec3(v[1].y - v[2].y, v[2].y - v[0].y, v[0].y - v[1].y) * d; 94 | derivatives.dbDy = vec3(v[2].x - v[1].x, v[0].x - v[2].x, v[1].x - v[0].x) * d; 95 | return derivatives; 96 | } 97 | 98 | // Takes draw call ID and primitive ID and returns the three patch control points 99 | Vertex[3] LoadTriangleVertices(uint drawID, uint primID) 100 | { 101 | Vertex[3] vertices; 102 | 103 | // Index of the first vertex of this draw call's geometry 104 | uint startIndex = drawID; // There's only one draw call at the moment, so drawID should always be 0. This would work out the offset of the first vertex in the nth draw call if using MultiDraw commands. 105 | 106 | // Get position in the Index Buffer of each vertex in this triangle (eg 31, 32, 33) 107 | uint triVert1IndexBufferPosition = (primID * 3 + 0) + startIndex; 108 | uint triVert2IndexBufferPosition = (primID * 3 + 1) + startIndex; 109 | uint triVert3IndexBufferPosition = (primID * 3 + 2) + startIndex; 110 | 111 | // Now get vertex index from index buffer (eg, 17, 41, 32) 112 | uint triVert0Index = indexBuffer[triVert1IndexBufferPosition].val; 113 | uint triVert1Index = indexBuffer[triVert2IndexBufferPosition].val; 114 | uint triVert2Index = indexBuffer[triVert3IndexBufferPosition].val; 115 | 116 | // Load vertex data of the 3 control points 117 | vertices[0] = vertexBuffer[triVert0Index]; 118 | vertices[1] = vertexBuffer[triVert1Index]; 119 | vertices[2] = vertexBuffer[triVert2Index]; 120 | 121 | return vertices; 122 | } 123 | 124 | void main() 125 | { 126 | // Unpack triangle ID and draw ID from visibility buffer 127 | vec4 visibilityRaw = subpassLoad(inputVisibility); 128 | uint DrawIdTriId = packUnorm4x8(visibilityRaw); 129 | 130 | // If this pixel doesn't contain triangle data, return early 131 | if (visibilityRaw != vec4(0.0)) 132 | { 133 | uint drawID = (DrawIdTriId >> 23) & 0x000000FF; // Draw ID the index of the draw call to which the triangle belongs 134 | uint triangleID = (DrawIdTriId & 0x007FFFFF) - 1; // Triangle ID is the offset of the triangle within the draw call. i.e. it is relative to drawID 135 | 136 | // Load triangle vertices using visibility buffer data 137 | Vertex[3] vertices = LoadTriangleVertices(drawID, triangleID); 138 | 139 | // Load position data of the 3 vertices 140 | vec3 vert0Pos = vertices[0].posXYZnormX.xyz; 141 | vec3 vert1Pos = vertices[1].posXYZnormX.xyz; 142 | vec3 vert2Pos = vertices[2].posXYZnormX.xyz; 143 | 144 | // Now displace each vertex by heightmap 145 | vert0Pos.y += texture(heightmap, vertices[0].normYZtexXY.zw / heightTexScale).r * heightScale; 146 | vert1Pos.y += texture(heightmap, vertices[1].normYZtexXY.zw / heightTexScale).r * heightScale; 147 | vert2Pos.y += texture(heightmap, vertices[2].normYZtexXY.zw / heightTexScale).r * heightScale; 148 | 149 | // Get normals for each Vertex 150 | vec3 vert0Norm = texture(normalmap, vertices[0].normYZtexXY.zw / heightTexScale).rgb; 151 | vec3 vert1Norm = texture(normalmap, vertices[1].normYZtexXY.zw / heightTexScale).rgb; 152 | vec3 vert2Norm = texture(normalmap, vertices[2].normYZtexXY.zw / heightTexScale).rgb; 153 | 154 | // Transform positions to clip space 155 | vec4 clipPos0 = ubo.mvp * vec4(vert0Pos, 1); 156 | vec4 clipPos1 = ubo.mvp * vec4(vert1Pos, 1); 157 | vec4 clipPos2 = ubo.mvp * vec4(vert2Pos, 1); 158 | 159 | // Pre-calculate 1 over w components 160 | vec3 oneOverW = 1.0 / vec3(clipPos0.w, clipPos1.w, clipPos2.w); 161 | 162 | // Calculate 2D screen positions 163 | clipPos0 *= oneOverW[0]; 164 | clipPos1 *= oneOverW[1]; 165 | clipPos2 *= oneOverW[2]; 166 | vec2 screenPositions[3] = { clipPos0.xy, clipPos1.xy, clipPos2.xy }; 167 | 168 | // Get barycentric coordinates for attribute interpolation 169 | DerivativesOutput derivatives = ComputePartialDerivatives(screenPositions); 170 | 171 | // Get delta vector that describes current screen point relative to vertex 0 172 | vec2 delta = inScreenPos + -screenPositions[0]; 173 | 174 | // Interpolate texture coordinates 175 | mat3x2 triTexCoords = 176 | { 177 | vec2 (vertices[0].normYZtexXY.zw), 178 | vec2 (vertices[1].normYZtexXY.zw), 179 | vec2 (vertices[2].normYZtexXY.zw) 180 | }; 181 | vec2 interpTexCoords = Interpolate2DAttributes(triTexCoords, derivatives.dbDx, derivatives.dbDy, delta); 182 | 183 | // Interpolate normal 184 | mat3 triNormals = 185 | { 186 | vert0Norm, 187 | vert1Norm, 188 | vert2Norm 189 | }; 190 | vec3 interpNorm = Interpolate3DAttributes(triNormals, derivatives.dbDx, derivatives.dbDy, delta); 191 | 192 | // Get fragment colour from texture 193 | vec4 textureDiffuseColour = texture(textureSampler, interpTexCoords); 194 | 195 | // Calculate directional light colour contribution 196 | vec4 lightColour = light.ambient; 197 | float lightIntensity = clamp(dot(-interpNorm, light.direction.xyz), 0.0f, 1.0f); 198 | lightColour += light.diffuse * lightIntensity; 199 | lightColour = clamp(lightColour, 0.0f, 1.0f); 200 | 201 | // Final Fragment colour 202 | outColour = clamp(textureDiffuseColour * lightColour, 0.0f, 1.0f); 203 | 204 | // Draw visibility buffer instead if setting is used. 205 | if (settings.showVisibilityBuffer == 1) 206 | outColour = visibilityRaw; 207 | else if (settings.showInterpolatedTexCoords == 1) 208 | outColour = vec4(normalize(abs(interpTexCoords)), 0.0f, 1.0f); 209 | //outColour = vec4(interpNorm, 1.0f); 210 | } 211 | else 212 | { 213 | outColour = vec4(0.35f, 0.55f, 0.7f, 1.0f); 214 | } 215 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/Image.cpp: -------------------------------------------------------------------------------- 1 | #include "Image.h" 2 | #include "VbtUtils.h" 3 | 4 | namespace vbt 5 | { 6 | void Image::Create(uint32_t imageWidth, uint32_t imageHeight, VkFormat imageFormat, VkImageTiling tiling, VkImageUsageFlags usage, VmaMemoryUsage memoryUsage, VkMemoryPropertyFlags properties, VmaAllocator& allocator) 7 | { 8 | // Store details 9 | format = imageFormat; 10 | width = imageWidth; 11 | height = imageHeight; 12 | 13 | VkImageCreateInfo imageInfo = {}; 14 | imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; 15 | imageInfo.imageType = VK_IMAGE_TYPE_2D; 16 | imageInfo.extent.width = SCAST_U32(width); 17 | imageInfo.extent.height = SCAST_U32(height); 18 | imageInfo.extent.depth = 1; 19 | imageInfo.mipLevels = 1; 20 | imageInfo.arrayLayers = 1; 21 | imageInfo.format = format; 22 | imageInfo.tiling = tiling; 23 | imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 24 | imageInfo.usage = usage; 25 | imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 26 | imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; 27 | imageInfo.flags = 0; // Optional 28 | 29 | // Create allocation info 30 | VmaAllocationCreateInfo allocInfo = {}; 31 | allocInfo.usage = memoryUsage; 32 | allocInfo.requiredFlags = properties; 33 | 34 | if (vmaCreateImage(allocator, &imageInfo, &allocInfo, &image, &imageMemory, nullptr) != VK_SUCCESS) 35 | { 36 | throw std::runtime_error("Failed to create image"); 37 | } 38 | } 39 | 40 | void Image::CreateImageView(const VkDevice device, VkImageAspectFlags aspectFlags) 41 | { 42 | VkImageViewCreateInfo viewInfo = {}; 43 | viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; 44 | viewInfo.image = image; 45 | viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; 46 | viewInfo.format = format; 47 | viewInfo.subresourceRange.aspectMask = aspectFlags; 48 | viewInfo.subresourceRange.baseMipLevel = 0; 49 | viewInfo.subresourceRange.levelCount = 1; 50 | viewInfo.subresourceRange.baseArrayLayer = 0; 51 | viewInfo.subresourceRange.layerCount = 1; 52 | 53 | if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) 54 | { 55 | throw std::runtime_error("Failed to create image view"); 56 | } 57 | } 58 | 59 | void Image::CreateSampler(VkDevice device, VkSamplerAddressMode addressMode) 60 | { 61 | VkSamplerCreateInfo samplerInfo = {}; 62 | samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; 63 | samplerInfo.magFilter = VK_FILTER_LINEAR; // Apply linear interpolation to over/under-sampled texels 64 | samplerInfo.minFilter = VK_FILTER_LINEAR; 65 | samplerInfo.addressModeU = addressMode; 66 | samplerInfo.addressModeV = addressMode; 67 | samplerInfo.addressModeW = addressMode; 68 | samplerInfo.anisotropyEnable = VK_TRUE; // Enable anisotropic filtering 69 | samplerInfo.maxAnisotropy = 16; 70 | samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; 71 | samplerInfo.unnormalizedCoordinates = VK_FALSE; // Clamp texel coordinates to [0, 1] 72 | samplerInfo.compareEnable = VK_FALSE; 73 | samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; 74 | samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; 75 | samplerInfo.mipLodBias = 0.0f; 76 | samplerInfo.minLod = 0.0f; 77 | samplerInfo.maxLod = 0.0f; 78 | 79 | if (vkCreateSampler(device, &samplerInfo, nullptr, &sampler) != VK_SUCCESS) 80 | { 81 | throw std::runtime_error("Failed to create texture sampler"); 82 | } 83 | } 84 | 85 | void Image::SetUpDescriptorInfo(VkImageLayout layout) 86 | { 87 | descriptor.imageLayout = layout; 88 | descriptor.imageView = imageView; 89 | descriptor.sampler = sampler; 90 | } 91 | 92 | 93 | void Image::SetUpDescriptorInfo(VkImageLayout layout, VkSampler sampler) 94 | { 95 | descriptor.imageLayout = layout; 96 | descriptor.imageView = imageView; 97 | descriptor.sampler = sampler; 98 | } 99 | 100 | void Image::SetupDescriptorWriteSet(VkDescriptorSet& dstSet, uint32_t binding, VkDescriptorType type, uint32_t count) 101 | { 102 | writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; 103 | writeDescriptorSet.dstSet = dstSet; 104 | writeDescriptorSet.dstBinding = binding; 105 | writeDescriptorSet.dstArrayElement = 0; 106 | writeDescriptorSet.descriptorType = type; 107 | writeDescriptorSet.descriptorCount = count; 108 | writeDescriptorSet.pImageInfo = &descriptor; 109 | } 110 | 111 | void Image::TransitionLayout(VkImageLayout srcLayout, VkImageLayout dstLayout, VkDevice device, PhysicalDevice physDevice, VkCommandPool& cmdPool) 112 | { 113 | VkCommandBuffer commandBuffer = BeginSingleTimeCommands(device, cmdPool); 114 | 115 | // Use an image memory barrier to perform the layout transition 116 | VkImageMemoryBarrier barrier = {}; 117 | barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; 118 | barrier.oldLayout = srcLayout; 119 | barrier.newLayout = dstLayout; 120 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // Don't bother transitioning queue family 121 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 122 | barrier.image = image; 123 | barrier.subresourceRange.baseMipLevel = 0; 124 | barrier.subresourceRange.levelCount = 1; 125 | barrier.subresourceRange.baseArrayLayer = 0; 126 | barrier.subresourceRange.layerCount = 1; 127 | // Determine aspect mask 128 | if (dstLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { 129 | barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; 130 | 131 | if (HasStencilComponent(format)) { 132 | barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; 133 | } 134 | } 135 | else { 136 | barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 137 | } 138 | 139 | // Since barriers are primarily used for synchronisation, we must specify which processes to wait on, and which processes should wait on this 140 | // We need to handle the transition from Undefined to Transfer Destination, from Transfer Destination to Shader Access and from Undefinded to Depth Attachment 141 | VkPipelineStageFlags sourceStage; 142 | VkPipelineStageFlags destinationStage; 143 | 144 | if (srcLayout == VK_IMAGE_LAYOUT_UNDEFINED && dstLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) 145 | { 146 | barrier.srcAccessMask = 0; 147 | barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; 148 | 149 | sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; 150 | destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; 151 | } 152 | else if (srcLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && dstLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) 153 | { 154 | barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; 155 | barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; 156 | 157 | sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; 158 | destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; 159 | } 160 | else if (srcLayout == VK_IMAGE_LAYOUT_UNDEFINED && dstLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) 161 | { 162 | barrier.srcAccessMask = 0; 163 | barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; 164 | 165 | sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; 166 | destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; 167 | } 168 | else 169 | { 170 | throw std::invalid_argument("Unsupported layout transition"); 171 | } 172 | 173 | vkCmdPipelineBarrier( 174 | commandBuffer, 175 | sourceStage, destinationStage, 176 | 0, 177 | 0, nullptr, 178 | 0, nullptr, 179 | 1, &barrier 180 | ); 181 | 182 | EndSingleTimeCommands(commandBuffer, device, physDevice, cmdPool); 183 | } 184 | 185 | void Image::CopyFromBuffer(VkBuffer buffer, VkDevice device, PhysicalDevice physDevice, VkCommandPool& cmdPool) 186 | { 187 | VkCommandBuffer commandBuffer = BeginSingleTimeCommands(device, cmdPool); 188 | 189 | VkBufferImageCopy region = {}; 190 | region.bufferOffset = 0; 191 | region.bufferRowLength = 0; 192 | region.bufferImageHeight = 0; 193 | 194 | region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 195 | region.imageSubresource.mipLevel = 0; 196 | region.imageSubresource.baseArrayLayer = 0; 197 | region.imageSubresource.layerCount = 1; 198 | 199 | region.imageOffset = { 0, 0, 0 }; 200 | region.imageExtent = { 201 | width, 202 | height, 203 | 1 204 | }; 205 | 206 | vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); 207 | 208 | EndSingleTimeCommands(commandBuffer, device, physDevice, cmdPool); 209 | } 210 | 211 | void Image::CleanUp(VmaAllocator& allocator, VkDevice device) 212 | { 213 | vkDestroySampler(device, sampler, nullptr); 214 | vkDestroyImageView(device, imageView, nullptr); 215 | vmaDestroyImage(allocator, image, imageMemory); 216 | } 217 | 218 | bool Image::HasStencilComponent(VkFormat format) 219 | { 220 | return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; 221 | } 222 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/SwapChain.cpp: -------------------------------------------------------------------------------- 1 | #include "SwapChain.h" 2 | 3 | namespace vbt 4 | { 5 | void SwapChain::InitSurface(GLFWwindow* window, VkInstance instance) 6 | { 7 | CreateSurface(window, instance); 8 | } 9 | 10 | void SwapChain::InitSwapChain(GLFWwindow* window, VkPhysicalDevice physicalDevice, VkDevice device) 11 | { 12 | CreateSwapChain(window, physicalDevice, device); 13 | CreateSwapImageViews(device); 14 | } 15 | 16 | void SwapChain::CleanUpSwapChain(VkDevice device) 17 | { 18 | // Destroy swapchain and its image views 19 | for (size_t i = 0; i < imageViews.size(); i++) { 20 | vkDestroyImageView(device, imageViews[i], nullptr); 21 | } 22 | vkDestroySwapchainKHR(device, swapChain, nullptr); 23 | } 24 | 25 | void SwapChain::CleanUpSurface(VkInstance instance) 26 | { 27 | vkDestroySurfaceKHR(instance, surface, nullptr); 28 | } 29 | 30 | void SwapChain::CreateSurface(GLFWwindow* window, VkInstance instance) 31 | { 32 | // GlfwCreateWindowSurface is platform agnostic and creates the surface object for the relevant platform under the hood 33 | // The required instance level Windows extensions are included by the glfwGetRequiredExtensions call. 34 | if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) 35 | { 36 | throw std::runtime_error("Failed to create window surface"); 37 | } 38 | } 39 | 40 | void SwapChain::CreateSwapChain(GLFWwindow* window, VkPhysicalDevice physicalDevice, VkDevice device) 41 | { 42 | SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(physicalDevice); 43 | 44 | // Choose optimal settings from supported details 45 | VkSurfaceFormatKHR surfaceFormat = ChooseSurfaceFormat(swapChainSupport.formats); 46 | VkPresentModeKHR presentMode = ChoosePresentMode(swapChainSupport.presentModes); 47 | VkExtent2D swapExtent = ChooseExtent(swapChainSupport.capabilities, window); 48 | 49 | // Set queue length 50 | uint32_t imageCount = swapChainSupport.capabilities.minImageCount; 51 | if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) 52 | { 53 | imageCount = swapChainSupport.capabilities.maxImageCount; 54 | } 55 | 56 | // Set up create info 57 | VkSwapchainCreateInfoKHR createInfo = {}; 58 | createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; 59 | createInfo.surface = surface; 60 | createInfo.minImageCount = imageCount; 61 | createInfo.imageFormat = surfaceFormat.format; 62 | createInfo.imageColorSpace = surfaceFormat.colorSpace; 63 | createInfo.imageExtent = swapExtent; 64 | createInfo.imageArrayLayers = 1; 65 | createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 66 | // Define how swap images are shared between queue families 67 | QueueFamilyIndices indices = PhysicalDevice::FindQueueFamilies(physicalDevice, surface); 68 | uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentationFamily.value() }; 69 | if (indices.graphicsFamily != indices.presentationFamily) 70 | { 71 | createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; 72 | createInfo.queueFamilyIndexCount = 2; 73 | createInfo.pQueueFamilyIndices = queueFamilyIndices; 74 | } 75 | else 76 | createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 77 | createInfo.preTransform = swapChainSupport.capabilities.currentTransform; 78 | createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 79 | createInfo.presentMode = presentMode; 80 | createInfo.clipped = VK_TRUE; // Maybe disable this later on for more consistent test results 81 | createInfo.oldSwapchain = VK_NULL_HANDLE; 82 | 83 | // Create swap chain 84 | if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) 85 | { 86 | throw std::runtime_error("Failed to create swap chain"); 87 | } 88 | 89 | // Retrieve swap chain image handles 90 | vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); 91 | images.resize(imageCount); 92 | vkGetSwapchainImagesKHR(device, swapChain, &imageCount, images.data()); 93 | 94 | // Store image format and resolution 95 | imageFormat = surfaceFormat.format; 96 | extent = swapExtent; 97 | } 98 | 99 | void SwapChain::CreateSwapImageViews(VkDevice device) 100 | { 101 | imageViews.resize(images.size()); 102 | 103 | for (uint32_t i = 0; i < images.size(); i++) 104 | { 105 | imageViews[i] = CreateImageView(device, images[i], imageFormat, VK_IMAGE_ASPECT_COLOR_BIT); 106 | } 107 | } 108 | 109 | SwapChainSupportDetails SwapChain::QuerySwapChainSupport(VkPhysicalDevice device) 110 | { 111 | SwapChainSupportDetails details; 112 | 113 | // Get basic surface capabilities 114 | vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); 115 | 116 | // Get supported surface formats 117 | uint32_t supportedFormatCount = 0; 118 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &supportedFormatCount, nullptr); 119 | if (supportedFormatCount > 0) 120 | { 121 | details.formats.resize(supportedFormatCount); 122 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &supportedFormatCount, details.formats.data()); 123 | } 124 | 125 | // Get supported presentation modes 126 | uint32_t supportedPresentModes = 0; 127 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &supportedPresentModes, nullptr); 128 | if (supportedPresentModes > 0) 129 | { 130 | details.presentModes.resize(supportedPresentModes); 131 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &supportedPresentModes, details.presentModes.data()); 132 | } 133 | 134 | return details; 135 | } 136 | 137 | 138 | VkSurfaceFormatKHR SwapChain::ChooseSurfaceFormat(const std::vector& availableFormats) 139 | { 140 | // Ideally the surface doesn't prefer any one format, so we can choose our own 141 | if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) { 142 | return { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR }; 143 | } 144 | 145 | // Otherwise we'll check for our preferred combination 146 | for (const auto& availableFormat : availableFormats) { 147 | if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { 148 | return availableFormat; 149 | } 150 | } 151 | 152 | // If that fails, it's usually ok to settle with the first specified format. Alternatively could rank them 153 | return availableFormats[0]; 154 | } 155 | 156 | // Present modes supported by Vulkan: 157 | // Immediate (tearing likely) 158 | // Fifo (V-sync) 159 | // Fifo relaxed (Doesn't wait for next v-blank if app is late. Tearing possible) 160 | // Mailbox (V-sync that replaces queued images when full. Can be used for triple buffering) 161 | VkPresentModeKHR SwapChain::ChoosePresentMode(const std::vector availablePresentModes) 162 | { 163 | // Only Fifo is guaranteed to be available, so use that as a default 164 | VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR; 165 | 166 | for (const auto& availablePresentMode : availablePresentModes) 167 | { 168 | // Prefer mailbox over Fifo 169 | if(availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) 170 | { 171 | return availablePresentMode; 172 | } 173 | } 174 | return bestMode; 175 | } 176 | 177 | // Swap extent is the resolution of the swap chain images. This is *almost* always exactly equal to 178 | // the target window size 179 | VkExtent2D SwapChain::ChooseExtent(const VkSurfaceCapabilitiesKHR& capabilities, GLFWwindow* window) 180 | { 181 | // Vulkan wants us to just return currentExtent, to match the window size. However some window managers 182 | // allow us to differ, and let us know by setting currentExtent to the max value of uint32_t. We obviously 183 | // have to check for that before returning currentExtent 184 | if (capabilities.currentExtent.width != std::numeric_limits::max()) 185 | { 186 | // Just return current extent (the exact size of the window) 187 | return capabilities.currentExtent; 188 | } 189 | else 190 | { 191 | // Current extent is useless. Return the window size, making sure its within the min/max range of the capabilities 192 | int width, height; 193 | glfwGetFramebufferSize(window, &width, &height); 194 | 195 | VkExtent2D actualExtent = { 196 | SCAST_U32(width), 197 | SCAST_U32(height) 198 | }; 199 | 200 | actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); 201 | actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); 202 | 203 | return actualExtent; 204 | } 205 | } 206 | 207 | VkImageView SwapChain::CreateImageView(const VkDevice &device, VkImage &image, VkFormat format, VkImageAspectFlags aspectFlags) 208 | { 209 | VkImageViewCreateInfo viewInfo = {}; 210 | viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; 211 | viewInfo.image = image; 212 | viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; 213 | viewInfo.format = format; 214 | viewInfo.subresourceRange.aspectMask = aspectFlags; 215 | viewInfo.subresourceRange.baseMipLevel = 0; 216 | viewInfo.subresourceRange.levelCount = 1; 217 | viewInfo.subresourceRange.baseArrayLayer = 0; 218 | viewInfo.subresourceRange.layerCount = 1; 219 | 220 | VkImageView imageView; 221 | if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) 222 | { 223 | throw std::runtime_error("Failed to create texture image view"); 224 | } 225 | 226 | return imageView; 227 | } 228 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/shaders/tessshade.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | #extension GL_ARB_shading_language_420pack : enable 4 | 5 | // Structs 6 | struct Vertex 7 | { 8 | vec4 posXYZnormX; 9 | vec4 normYZtexXY; 10 | }; 11 | struct Index 12 | { 13 | uint val; 14 | }; 15 | struct DerivativesOutput 16 | { 17 | vec3 dbDx; 18 | vec3 dbDy; 19 | }; 20 | 21 | // Constants 22 | const float heightTexScale = 8.0f; 23 | const float heightScale = 5.0f; 24 | 25 | // In 26 | layout(location = 0) in vec2 inScreenPos; 27 | 28 | // Out 29 | layout(location = 0) out vec4 outColour; 30 | 31 | // Descriptors 32 | layout (set = 0, binding = 0) uniform sampler2D textureSampler; 33 | layout (input_attachment_index = 0, set = 0, binding = 1) uniform subpassInput inputVisibility; 34 | layout (input_attachment_index = 1, set = 0, binding = 9) uniform subpassInput inputTessCoords1; 35 | layout (input_attachment_index = 1, set = 0, binding = 10) uniform subpassInput inputTessCoords2; 36 | layout (input_attachment_index = 1, set = 0, binding = 11) uniform subpassInput inputTessCoords3; 37 | layout(set = 0, binding = 2) uniform MVPUniformBufferObject 38 | { 39 | mat4 mvp; 40 | mat4 proj; 41 | } ubo; 42 | layout (std430, set = 0, binding = 3) readonly buffer IndxBuff 43 | { 44 | Index indexBuffer[]; 45 | }; 46 | layout (std430, set = 0, binding = 4) readonly buffer VertBuff 47 | { 48 | Vertex vertexBuffer[]; 49 | }; 50 | layout(set = 0, binding = 5) uniform SettingsUniformBufferObject 51 | { 52 | uint tessellationFactor; 53 | uint showVisibilityBuffer; 54 | uint showTessCoordsBuffer; 55 | uint showInterpolatedTexCoords; 56 | uint wireframe; 57 | } settings; 58 | layout(set = 0, binding = 6) uniform sampler2D heightmap; 59 | layout(set = 0, binding = 7) uniform sampler2D normalmap; 60 | layout(set = 0, binding = 8) uniform DirectionalLightUniformBufferObject 61 | { 62 | vec4 direction; 63 | vec4 ambient; 64 | vec4 diffuse; 65 | } light; 66 | 67 | vec2 Interpolate2DLinear(vec2 v0, vec2 v1, vec2 v2, vec3 tessCoord) 68 | { 69 | return vec2(tessCoord.x) * v0 + vec2(tessCoord.y) * v1 + vec2(tessCoord.z) * v2; 70 | } 71 | 72 | vec3 Interpolate3DLinear(vec3 v0, vec3 v1, vec3 v2, vec3 tessCoord) 73 | { 74 | return vec3(tessCoord.x) * v0 + vec3(tessCoord.y) * v1 + vec3(tessCoord.z) * v2; 75 | } 76 | 77 | // Interpolate 2D attributes using the partial derivatives and generates dx and dy for texture sampling. 78 | vec2 Interpolate2DAttributes(mat3x2 attributes, vec3 dbDx, vec3 dbDy, vec2 d) 79 | { 80 | vec3 attr0 = vec3(attributes[0].x, attributes[1].x, attributes[2].x); 81 | vec3 attr1 = vec3(attributes[0].y, attributes[1].y, attributes[2].y); 82 | vec2 attribute_x = vec2(dot(dbDx,attr0), dot(dbDx,attr1)); 83 | vec2 attribute_y = vec2(dot(dbDy,attr0), dot(dbDy,attr1)); 84 | vec2 attribute_s = attributes[0]; 85 | 86 | vec2 result = (attribute_s + d.x * attribute_x + d.y * attribute_y); 87 | return result; 88 | } 89 | 90 | // Interpolate vertex attributes at point 'd' using the partial derivatives 91 | vec3 Interpolate3DAttributes(mat3 attributes, vec3 dbDx, vec3 dbDy, vec2 d) 92 | { 93 | vec3 attribute_x = attributes * dbDx; 94 | vec3 attribute_y = attributes * dbDy; 95 | vec3 attribute_s = attributes[0]; 96 | 97 | return (attribute_s + d.x * attribute_x + d.y * attribute_y); 98 | } 99 | 100 | // Engel's barycentric coord partial derivs function. Follows equation from [Schied][Dachsbacher] 101 | // Computes the partial derivatives of point's barycentric coordinates from the projected screen space vertices 102 | DerivativesOutput ComputePartialDerivatives(vec2 v[3]) 103 | { 104 | DerivativesOutput derivatives; 105 | float d = 1.0 / determinant(mat2(v[2] - v[1], v[0] - v[1])); 106 | derivatives.dbDx = vec3(v[1].y - v[2].y, v[2].y - v[0].y, v[0].y - v[1].y) * d; 107 | derivatives.dbDy = vec3(v[2].x - v[1].x, v[0].x - v[2].x, v[1].x - v[0].x) * d; 108 | return derivatives; 109 | } 110 | 111 | // Takes draw call ID and primitive ID and returns the three patch control points 112 | Vertex[3] LoadPatchControlPoints(uint drawID, uint primID) 113 | { 114 | Vertex[3] controlPoints; 115 | 116 | // Index of the first vertex of this draw call's geometry 117 | uint startIndex = drawID; // There's only one draw call at the moment, so drawID should always be 0 118 | 119 | // Get position in the Index Buffer of each vertex in this triangle (eg 31, 32, 33) 120 | uint triVert1IndexBufferPosition = (primID * 3 + 0) + startIndex; 121 | uint triVert2IndexBufferPosition = (primID * 3 + 1) + startIndex; 122 | uint triVert3IndexBufferPosition = (primID * 3 + 2) + startIndex; 123 | 124 | // Now get vertex index from index buffer (eg, 17, 41, 32) 125 | uint triVert0Index = indexBuffer[triVert1IndexBufferPosition].val; 126 | uint triVert1Index = indexBuffer[triVert2IndexBufferPosition].val; 127 | uint triVert2Index = indexBuffer[triVert3IndexBufferPosition].val; 128 | 129 | // Load vertex data of the 3 control points 130 | controlPoints[0] = vertexBuffer[triVert0Index]; 131 | controlPoints[1] = vertexBuffer[triVert1Index]; 132 | controlPoints[2] = vertexBuffer[triVert2Index]; 133 | 134 | return controlPoints; 135 | } 136 | 137 | // Re-evaluates the evaluation stage for the tessellated primitive this fragment belongs to. 138 | // Takes input patch control points and interpolates to tessellated vertices with stored 139 | // tessellation coordinates 140 | Vertex[3] EvaluateTessellatedPrimitive(Vertex[3] patchControlPoints, vec4 tessCoords_v1XYZ_v2X, vec4 tessCoords_v2YZ_v3XY, float tessCoords_v3Z) 141 | { 142 | Vertex[3] vertices; 143 | 144 | // Extract tessellation (barycentric) coordinates for the three vertices 145 | vec3 tessCoord0 = tessCoords_v1XYZ_v2X.xyz; 146 | vec3 tessCoord1 = vec3(tessCoords_v1XYZ_v2X.w, tessCoords_v2YZ_v3XY.xy); 147 | vec3 tessCoord2 = vec3(tessCoords_v2YZ_v3XY.zw, tessCoords_v3Z); 148 | 149 | // Interpolate positions 150 | vertices[0].posXYZnormX.xyz = Interpolate3DLinear(patchControlPoints[0].posXYZnormX.xyz, patchControlPoints[1].posXYZnormX.xyz, patchControlPoints[2].posXYZnormX.xyz, tessCoord0); 151 | vertices[1].posXYZnormX.xyz = Interpolate3DLinear(patchControlPoints[0].posXYZnormX.xyz, patchControlPoints[1].posXYZnormX.xyz, patchControlPoints[2].posXYZnormX.xyz, tessCoord1); 152 | vertices[2].posXYZnormX.xyz = Interpolate3DLinear(patchControlPoints[0].posXYZnormX.xyz, patchControlPoints[1].posXYZnormX.xyz, patchControlPoints[2].posXYZnormX.xyz, tessCoord2); 153 | 154 | // Not bothering with normals until lighting is implemented 155 | vertices[0].posXYZnormX.w = 0.0; vertices[0].normYZtexXY.xy = vec2(0.0, 1.0); 156 | vertices[1].posXYZnormX.w = 0.0; vertices[1].normYZtexXY.xy = vec2(0.0, 1.0); 157 | vertices[2].posXYZnormX.w = 0.0; vertices[2].normYZtexXY.xy = vec2(0.0, 1.0); 158 | 159 | // Interpolate UV coordinates 160 | vertices[0].normYZtexXY.zw = Interpolate2DLinear(patchControlPoints[0].normYZtexXY.zw, patchControlPoints[1].normYZtexXY.zw, patchControlPoints[2].normYZtexXY.zw, tessCoord0); 161 | vertices[1].normYZtexXY.zw = Interpolate2DLinear(patchControlPoints[0].normYZtexXY.zw, patchControlPoints[1].normYZtexXY.zw, patchControlPoints[2].normYZtexXY.zw, tessCoord1); 162 | vertices[2].normYZtexXY.zw = Interpolate2DLinear(patchControlPoints[0].normYZtexXY.zw, patchControlPoints[1].normYZtexXY.zw, patchControlPoints[2].normYZtexXY.zw, tessCoord2); 163 | 164 | return vertices; 165 | } 166 | 167 | void main() 168 | { 169 | // Unpack triangle ID and draw ID from visibility buffer 170 | vec4 visibilityRaw = subpassLoad(inputVisibility); 171 | vec4 tessCoords_v1XYZ_v2X = subpassLoad(inputTessCoords1); 172 | vec4 tessCoords_v2YZ_v3XY = subpassLoad(inputTessCoords2); 173 | float tessCoords_v3Z = subpassLoad(inputTessCoords3).x; 174 | uint DrawIdTriId = packUnorm4x8(visibilityRaw); 175 | 176 | // If this pixel doesn't contain triangle data, return early 177 | if (visibilityRaw != vec4(0.0)) 178 | { 179 | // Output debug tess coords 180 | vec4 tessCoordsColour = vec4(packUnorm4x8(vec4(tessCoords_v1XYZ_v2X.xyz, 0)), packUnorm4x8(vec4(tessCoords_v1XYZ_v2X.w, tessCoords_v2YZ_v3XY.xy, 0)), packUnorm4x8(vec4(tessCoords_v2YZ_v3XY.zw, tessCoords_v3Z, 0)), 1.0); 181 | tessCoordsColour = normalize(tessCoordsColour); 182 | 183 | uint drawID = (DrawIdTriId >> 23) & 0x000000FF; // Draw ID the number of draw call to which the triangle belongs 184 | uint triangleID = (DrawIdTriId & 0x007FFFFF) - 1; // Triangle ID is the offset of the triangle within the draw call. i.e. it is relative to drawID 185 | 186 | // Load input patch control points using visibility buffer data 187 | Vertex[3] patchControlPoints = LoadPatchControlPoints(drawID, triangleID); 188 | 189 | // Now interpolate to the generated tessellation primitive using stored tess coords 190 | Vertex[3] primitiveVertices = EvaluateTessellatedPrimitive(patchControlPoints, tessCoords_v1XYZ_v2X, tessCoords_v2YZ_v3XY, tessCoords_v3Z); 191 | 192 | // Get position data of vertices 193 | vec3 vertPos0 = primitiveVertices[0].posXYZnormX.xyz; 194 | vec3 vertPos1 = primitiveVertices[1].posXYZnormX.xyz; 195 | vec3 vertPos2 = primitiveVertices[2].posXYZnormX.xyz; 196 | 197 | // Now displace each vertex by heightmap 198 | vertPos0.y += texture(heightmap, primitiveVertices[0].normYZtexXY.zw / heightTexScale).r * heightScale; 199 | vertPos1.y += texture(heightmap, primitiveVertices[1].normYZtexXY.zw / heightTexScale).r * heightScale; 200 | vertPos2.y += texture(heightmap, primitiveVertices[2].normYZtexXY.zw / heightTexScale).r * heightScale; 201 | 202 | // Get normals for each Vertex 203 | vec3 vert0Norm = texture(normalmap, primitiveVertices[0].normYZtexXY.zw / heightTexScale).rgb; 204 | vec3 vert1Norm = texture(normalmap, primitiveVertices[1].normYZtexXY.zw / heightTexScale).rgb; 205 | vec3 vert2Norm = texture(normalmap, primitiveVertices[2].normYZtexXY.zw / heightTexScale).rgb; 206 | 207 | // Transform positions to clip space 208 | vec4 clipPos0 = ubo.mvp * vec4(vertPos0, 1); 209 | vec4 clipPos1 = ubo.mvp * vec4(vertPos1, 1); 210 | vec4 clipPos2 = ubo.mvp * vec4(vertPos2, 1); 211 | 212 | // Pre-calculate 1 over w components 213 | vec3 oneOverW = 1.0 / vec3(clipPos0.w, clipPos1.w, clipPos2.w); 214 | 215 | // Calculate 2D screen positions 216 | clipPos0 *= oneOverW[0]; 217 | clipPos1 *= oneOverW[1]; 218 | clipPos2 *= oneOverW[2]; 219 | vec2 screenPositions[3] = { clipPos0.xy, clipPos1.xy, clipPos2.xy }; 220 | 221 | // Get barycentric coordinates for attribute interpolation 222 | DerivativesOutput derivatives = ComputePartialDerivatives(screenPositions); 223 | 224 | // Get delta vector that describes current screen point relative to vertex 0 225 | vec2 delta = inScreenPos + -screenPositions[0]; 226 | 227 | // Interpolate texture coordinates 228 | mat3x2 triTexCoords = 229 | { 230 | vec2 (primitiveVertices[0].normYZtexXY.z, primitiveVertices[0].normYZtexXY.w), 231 | vec2 (primitiveVertices[1].normYZtexXY.z, primitiveVertices[1].normYZtexXY.w), 232 | vec2 (primitiveVertices[2].normYZtexXY.z, primitiveVertices[2].normYZtexXY.w) 233 | }; 234 | vec2 interpTexCoords = Interpolate2DAttributes(triTexCoords, derivatives.dbDx, derivatives.dbDy, delta); 235 | 236 | // Interpolate normal 237 | mat3 triNormals = 238 | { 239 | vert0Norm, 240 | vert1Norm, 241 | vert2Norm 242 | }; 243 | vec3 interpNorm = Interpolate3DAttributes(triNormals, derivatives.dbDx, derivatives.dbDy, delta); 244 | 245 | // Get fragment colour from texture 246 | vec4 textureDiffuseColour = texture(textureSampler, interpTexCoords); 247 | 248 | // Calculate directional light colour contribution 249 | vec4 lightColour = light.ambient; 250 | float lightIntensity = clamp(dot(-interpNorm, light.direction.xyz), 0.0f, 1.0f); 251 | lightColour += light.diffuse * lightIntensity; 252 | lightColour = clamp(lightColour, 0.0f, 1.0f); 253 | 254 | // Final Fragment colour 255 | outColour = clamp(textureDiffuseColour * lightColour, 0.0f, 1.0f); 256 | 257 | // Draw visibility buffer images instead if settings are used. 258 | if (settings.showVisibilityBuffer == 1) 259 | outColour = visibilityRaw; 260 | else if (settings.showTessCoordsBuffer == 1) 261 | outColour = tessCoordsColour; 262 | else if (settings.showInterpolatedTexCoords == 1) 263 | outColour = vec4(normalize(abs(interpTexCoords)), 0.0f, 1.0f); 264 | //outColour = vec4(interpNorm, 1.0f); 265 | } 266 | else 267 | { 268 | outColour = vec4(0.35f, 0.55f, 0.7f, 1.0f); 269 | } 270 | 271 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/VulkanCore.cpp: -------------------------------------------------------------------------------- 1 | #include "VulkanCore.h" 2 | #include "VbtUtils.h" 3 | 4 | namespace vbt 5 | { 6 | #pragma region Interface Functions 7 | void VulkanCore::Init(GLFWwindow* window) 8 | { 9 | CreateInstance(); 10 | SetupDebugCallback(); 11 | swapChain.InitSurface(window, instance); 12 | physicalDevice.Init(instance, swapChain.Surface()); 13 | CreateLogicalDevice(); 14 | swapChain.InitSwapChain(window, physicalDevice.VkHandle(), device); 15 | CreateSynchronisationObjects(); 16 | } 17 | 18 | void VulkanCore::RecreateSwapchain(GLFWwindow* window) 19 | { 20 | swapChain.InitSwapChain(window, physicalDevice.VkHandle(), device); 21 | } 22 | 23 | void VulkanCore::CleanUp() 24 | { 25 | // Destroy Sync Objects 26 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) 27 | { 28 | vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); 29 | vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); 30 | vkDestroyFence(device, inFlightFences[i], nullptr); 31 | } 32 | 33 | // Destroy swapchain 34 | swapChain.CleanUpSwapChain(device); 35 | 36 | // Destroy Logical Device 37 | vkDestroyDevice(device, nullptr); 38 | 39 | // Destroy debug callback 40 | if (enableValidationLayers) 41 | DestroyDebutUtilsMessngerEXT(instance, callback, nullptr); 42 | 43 | // Destroy window surface 44 | swapChain.CleanUpSurface(instance); 45 | 46 | // Destroy Instance last 47 | vkDestroyInstance(instance, nullptr); 48 | } 49 | 50 | void VulkanCore::CreateInstance() 51 | { 52 | // Check that required validation layers exist 53 | if (enableValidationLayers && !CheckValidationLayerSupport()) 54 | throw std::runtime_error("Requested validation layers missing"); 55 | 56 | // Provide some information about the app (optional) 57 | VkApplicationInfo appInfo = {}; 58 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 59 | appInfo.pApplicationName = "Visibility Buffer Tessellation"; 60 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 61 | appInfo.pEngineName = "No Engine"; 62 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 63 | appInfo.apiVersion = VK_API_VERSION_1_0; 64 | 65 | // Set up instance create info (required) 66 | VkInstanceCreateInfo createInfo = {}; 67 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 68 | createInfo.pApplicationInfo = &appInfo; 69 | 70 | // Get extensions 71 | auto extensions = GetRequiredInstanceExtensions(); 72 | createInfo.enabledExtensionCount = SCAST_U32(extensions.size()); 73 | createInfo.ppEnabledExtensionNames = extensions.data(); 74 | 75 | // Specify validation layers to enable 76 | if (enableValidationLayers) 77 | { 78 | createInfo.enabledLayerCount = SCAST_U32(validationLayers.size()); 79 | createInfo.ppEnabledLayerNames = validationLayers.data(); 80 | } 81 | else 82 | createInfo.enabledLayerCount = 0; 83 | 84 | // Create instance 85 | VkResult instanceResult = vkCreateInstance(&createInfo, nullptr, &instance); 86 | if (instanceResult != VK_SUCCESS) 87 | { 88 | throw std::runtime_error("Failed to create instance"); 89 | } 90 | } 91 | #pragma endregion 92 | 93 | #pragma region Validation Layers and Extensions 94 | //Checks if the validation layers specified in the header file are available for loading 95 | bool VulkanCore::CheckValidationLayerSupport() 96 | { 97 | // Get list of all available layers 98 | uint32_t supportedLayerCount; 99 | vkEnumerateInstanceLayerProperties(&supportedLayerCount, nullptr); 100 | std::vector supportedLayers(supportedLayerCount); 101 | vkEnumerateInstanceLayerProperties(&supportedLayerCount, supportedLayers.data()); 102 | 103 | // Now check if the layers we want exist in supported layers 104 | std::set requiredLayers(validationLayers.begin(), validationLayers.end()); 105 | for (const auto& layer : supportedLayers) 106 | { 107 | requiredLayers.erase(layer.layerName); 108 | } 109 | 110 | // If requiredLayers is empty then all of them are supported 111 | return requiredLayers.empty(); 112 | } 113 | 114 | std::vector VulkanCore::GetRequiredInstanceExtensions() 115 | { 116 | // Get extensions required by glfw 117 | uint32_t glfwExtensionCount = 0; 118 | const char** glfwExtensions; 119 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 120 | 121 | // Check that they exist 122 | if (!CheckForRequiredGlfwExtensions(glfwExtensions, glfwExtensionCount)) 123 | throw std::runtime_error("GLFW Required Extension Missing"); 124 | 125 | // Create extensions list 126 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 127 | 128 | // Add any more we need (existence is implied by the availability of validation layers) 129 | if (enableValidationLayers) 130 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 131 | 132 | return extensions; 133 | } 134 | 135 | bool VulkanCore::CheckForRequiredGlfwExtensions(const char** glfwExtensions, uint32_t glfwExtensionCount) 136 | { 137 | // Find all supported extensions 138 | uint32_t extensionCount = 0; 139 | vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); 140 | std::vector extensions(extensionCount); 141 | vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); 142 | 143 | // For each required extensions 144 | for (uint32_t i = 0; i < glfwExtensionCount; i++) 145 | { 146 | bool requiredExtensionFound = false; 147 | 148 | // Check each supported extension 149 | for (const auto& extension : extensions) 150 | { 151 | if (strcmp(extension.extensionName, glfwExtensions[i]) == 0) 152 | { 153 | requiredExtensionFound = true; 154 | break; 155 | } 156 | } 157 | 158 | if (!requiredExtensionFound) 159 | { 160 | return false; 161 | } 162 | } 163 | 164 | return true; 165 | } 166 | #pragma endregion 167 | 168 | #pragma region Device Functions 169 | void VulkanCore::CreateLogicalDevice() 170 | { 171 | QueueFamilyIndices indices = PhysicalDevice::FindQueueFamilies(physicalDevice.VkHandle(), swapChain.Surface()); 172 | 173 | // Create a DeviceQueueCreateInfo for each required queue family 174 | std::vector queueCreateInfos; 175 | std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentationFamily.value() }; 176 | 177 | // Set priority for command buffer execution scheduling 178 | float queuePriority = 1.0f; 179 | for (uint32_t queueFamily : uniqueQueueFamilies) 180 | { 181 | VkDeviceQueueCreateInfo queueCreateInfo = {}; 182 | queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 183 | queueCreateInfo.queueFamilyIndex = queueFamily; 184 | queueCreateInfo.queueCount = 1; 185 | queueCreateInfo.pQueuePriorities = &queuePriority; 186 | queueCreateInfos.push_back(queueCreateInfo); 187 | } 188 | 189 | // Define required device features 190 | VkPhysicalDeviceFeatures deviceFeatures = {}; 191 | deviceFeatures.samplerAnisotropy = VK_TRUE; 192 | deviceFeatures.geometryShader = VK_TRUE; 193 | deviceFeatures.tessellationShader = VK_TRUE; 194 | deviceFeatures.fragmentStoresAndAtomics = VK_TRUE; 195 | 196 | // Create the logical device 197 | VkDeviceCreateInfo createInfo = {}; 198 | createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 199 | createInfo.queueCreateInfoCount = SCAST_U32(queueCreateInfos.size()); 200 | createInfo.pQueueCreateInfos = queueCreateInfos.data(); 201 | createInfo.pEnabledFeatures = &deviceFeatures; 202 | createInfo.enabledExtensionCount = SCAST_U32(deviceExtensions.size()); 203 | createInfo.ppEnabledExtensionNames = deviceExtensions.data(); 204 | 205 | if (enableValidationLayers) 206 | { 207 | createInfo.enabledLayerCount = SCAST_U32(validationLayers.size()); 208 | createInfo.ppEnabledLayerNames = validationLayers.data(); 209 | } 210 | else 211 | createInfo.enabledLayerCount = 0; 212 | 213 | // Create the logical device 214 | if (vkCreateDevice(physicalDevice.VkHandle(), &createInfo, nullptr, &device) != VK_SUCCESS) 215 | { 216 | throw std::runtime_error("Failed to create logical device"); 217 | } 218 | 219 | // Retrieve queue handles 220 | vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &physicalDevice.Queues()->graphics); 221 | vkGetDeviceQueue(device, indices.presentationFamily.value(), 0, &physicalDevice.Queues()->present); 222 | } 223 | #pragma endregion 224 | 225 | #pragma region Presentation Functions 226 | // We'll need to set up semaphores to ensure the order of the asynchronous functions 227 | void VulkanCore::CreateSynchronisationObjects() 228 | { 229 | imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); 230 | renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); 231 | inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); 232 | 233 | VkSemaphoreCreateInfo semaphoreInfo = {}; 234 | semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; 235 | 236 | VkFenceCreateInfo fenceInfo = {}; 237 | fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; 238 | fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; // Signal the fence so that the first frame is rendered 239 | 240 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) 241 | { 242 | if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS || 243 | vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS || 244 | vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) 245 | { 246 | 247 | throw std::runtime_error("Failed to create syncrhonisation objects for a frame"); 248 | } 249 | } 250 | } 251 | #pragma endregion 252 | 253 | #pragma region Debug Functions 254 | void VulkanCore::SetupDebugCallback() 255 | { 256 | // If we're not using validation layers then we don't need a callback 257 | if (!enableValidationLayers) return; 258 | 259 | VkDebugUtilsMessengerCreateInfoEXT createInfo = {}; 260 | createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; 261 | 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; 262 | 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; 263 | createInfo.pfnUserCallback = DebugCallback; 264 | createInfo.pUserData = nullptr; // Optional 265 | 266 | // Set up object from external function 267 | if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) 268 | throw std::runtime_error("Failed to setup debug callback!"); 269 | } 270 | 271 | VKAPI_ATTR VkBool32 VKAPI_CALL VulkanCore::DebugCallback( 272 | VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, 273 | VkDebugUtilsMessageTypeFlagsEXT messageType, 274 | const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, 275 | void* pUserData) 276 | { 277 | std::string messageTypeStr = ""; 278 | switch (messageType) 279 | { 280 | case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: 281 | { 282 | messageTypeStr = "General"; 283 | break; 284 | } 285 | case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: 286 | { 287 | messageTypeStr = "Violation"; 288 | break; 289 | } 290 | case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: 291 | { 292 | messageTypeStr = "Performance"; 293 | break; 294 | } 295 | } 296 | std::cerr << "Validation Layer Message (" << messageTypeStr.c_str() << "): " << pCallbackData->pMessage << std::endl; 297 | 298 | return VK_FALSE; 299 | } 300 | 301 | // Gets the external function from extension to set up the messenger object 302 | VkResult VulkanCore::CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback) 303 | { 304 | auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 305 | if (func != nullptr) { 306 | return func(instance, pCreateInfo, pAllocator, pCallback); 307 | } 308 | else { 309 | return VK_ERROR_EXTENSION_NOT_PRESENT; 310 | } 311 | } 312 | 313 | // Loads external function for destroying messenger object and executes it 314 | void VulkanCore::DestroyDebutUtilsMessngerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator) 315 | { 316 | auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 317 | if (func != nullptr) { 318 | func(instance, callback, pAllocator); 319 | } 320 | } 321 | #pragma endregion 322 | 323 | } // Namespace -------------------------------------------------------------------------------- /VisibilityBufferTessellation/VbtImGUI.cpp: -------------------------------------------------------------------------------- 1 | #include "VbtImGUI.h" 2 | #include "VulkanApplication.h" 3 | #include "VbtUtils.h" 4 | 5 | namespace vbt 6 | { 7 | void ImGUI::Init(VulkanApplication* app, GLFWwindow* window, ImGui_ImplVulkan_InitInfo* info, VkRenderPass renderPass, VkCommandPool commandPool, int visBuffTriCount, int tessTriCount) 8 | { 9 | // Store app instance and triangle counts 10 | appHandle = app; 11 | this->visBuffTriCount = visBuffTriCount; 12 | this->tessTricount = tessTriCount; // Tri-count in host memory won't change, subdivision is calculated locally per-frame 13 | 14 | // Create vulkan resources 15 | CreateVulkanResources(); 16 | 17 | // Setup Dear ImGui context 18 | IMGUI_CHECKVERSION(); 19 | ImGui::CreateContext(); 20 | ImGuiIO& io = ImGui::GetIO(); (void)io; 21 | 22 | // Setup Style 23 | ImGui::StyleColorsDark(); 24 | 25 | // Setup bindings to vulkan objects 26 | ImGui_ImplGlfw_InitForVulkan(window, true); 27 | ImGui_ImplVulkan_InitInfo initInfo = *info; 28 | initInfo.DescriptorPool = descriptorPool; 29 | ImGui_ImplVulkanVbt_Init(&initInfo, renderPass); 30 | 31 | // Load Fonts 32 | vbt::PhysicalDevice physDevice = appHandle->GetVulkanCore()->PhysDevice(); 33 | VkCommandBuffer fontCmd = BeginSingleTimeCommands(info->Device, commandPool); 34 | ImGui_ImplVulkan_CreateFontsTexture(fontCmd); 35 | EndSingleTimeCommands(fontCmd, info->Device, physDevice, commandPool); 36 | ImGui_ImplVulkan_InvalidateFontUploadObjects(); 37 | } 38 | 39 | // Reinitialises ImGui for new render pass configuration 40 | void ImGUI::Recreate(ImGui_ImplVulkan_InitInfo* info, VkRenderPass renderPass, VkCommandPool commandPool) 41 | { 42 | vkDestroyDescriptorPool(appHandle->GetVulkanCore()->Device(), descriptorPool, nullptr); 43 | ImGui_ImplVulkan_Shutdown(); 44 | 45 | CreateVulkanResources(); 46 | 47 | ImGui_ImplVulkan_InitInfo initInfo = *info; 48 | initInfo.DescriptorPool = descriptorPool; 49 | ImGui_ImplVulkanVbt_Init(&initInfo, renderPass); 50 | 51 | // Load Fonts 52 | vbt::PhysicalDevice physDevice = appHandle->GetVulkanCore()->PhysDevice(); 53 | VkCommandBuffer fontCmd = BeginSingleTimeCommands(info->Device, commandPool); 54 | ImGui_ImplVulkan_CreateFontsTexture(fontCmd); 55 | EndSingleTimeCommands(fontCmd, info->Device, physDevice, commandPool); 56 | ImGui_ImplVulkan_InvalidateFontUploadObjects(); 57 | } 58 | 59 | void ImGUI::CreateVulkanResources() 60 | { 61 | // Descriptor Pool 62 | std::array imGuiPoolSizes = {}; 63 | imGuiPoolSizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; 64 | imGuiPoolSizes[0].descriptorCount = 1; 65 | VkDescriptorPoolCreateInfo imGuiPoolInfo = {}; 66 | imGuiPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; 67 | imGuiPoolInfo.poolSizeCount = SCAST_U32(imGuiPoolSizes.size()); 68 | imGuiPoolInfo.pPoolSizes = imGuiPoolSizes.data(); 69 | imGuiPoolInfo.maxSets = SCAST_U32(appHandle->GetVulkanCore()->Swapchain().Images().size()); 70 | if (vkCreateDescriptorPool(appHandle->GetVulkanCore()->Device(), &imGuiPoolInfo, nullptr, &descriptorPool) != VK_SUCCESS) 71 | { 72 | throw std::runtime_error("Failed to create ImGui descriptor pool"); 73 | } 74 | } 75 | 76 | // Define UI elements to display 77 | void ImGUI::Update(double frameTime, double forwardTime, double deferredTime, glm::vec3 cameraPos, glm::vec3 cameraRot, glm::vec3 lightDirection, glm::vec4 lightDiffuse, glm::vec4 lightAmbient) 78 | { 79 | // Store local app settings 80 | static AppSettings currentSettings; 81 | currentSettings.cameraPos = cameraPos; 82 | currentSettings.cameraRot.x = WrapAngle(cameraRot.x); 83 | currentSettings.cameraRot.y = WrapAngle(cameraRot.y); 84 | currentSettings.cameraRot.z = WrapAngle(cameraRot.z); 85 | currentSettings.lightDiffuse = lightDiffuse; 86 | currentSettings.lightDirection = lightDirection; 87 | currentSettings.lightAmbient = lightAmbient; 88 | 89 | // Calculate tessellation tri-count at current tess factor 90 | int tessCount = tessTricount * CalculateTriangleSubdivision(currentSettings.tessellationFactor); 91 | 92 | // Start the Dear ImGui frame 93 | ImGui_ImplVulkan_NewFrame(); 94 | ImGui_ImplGlfw_NewFrame(); 95 | ImGui::NewFrame(); 96 | 97 | // Update pass times 98 | std::rotate(frameTimes.begin(), frameTimes.begin() + 1, frameTimes.end()); // Moves whole collection down one, moving first term to the back to be overwritten 99 | frameTime *= 1000.0; 100 | frameTimes.back() = (float)frameTime; 101 | if (frameTime < frameTimeMin) { 102 | frameTimeMin = frameTime; 103 | } 104 | if (frameTime > frameTimeMax) { 105 | frameTimeMax = frameTime; 106 | } 107 | char frameTimeChar[64]; 108 | snprintf(frameTimeChar, sizeof frameTimeChar, "%.2f", frameTime); 109 | 110 | std::rotate(forwardTimes.begin(), forwardTimes.begin() + 1, forwardTimes.end()); 111 | forwardTimes.back() = (float)forwardTime; 112 | if (forwardTime < forwardTimeMin) { 113 | forwardTimeMin = forwardTime; 114 | } 115 | if (forwardTime > forwardTimeMax) { 116 | forwardTimeMax = forwardTime; 117 | } 118 | char forwardTimeChar[64]; 119 | snprintf(forwardTimeChar, sizeof forwardTimeChar, "%.2f", forwardTime); 120 | 121 | std::rotate(deferredTimes.begin(), deferredTimes.begin() + 1, deferredTimes.end()); 122 | deferredTimes.back() = (float)deferredTime; 123 | if (deferredTime < deferredTimeMin) { 124 | deferredTimeMin = deferredTime; 125 | } 126 | if (deferredTime > deferredTimeMax) { 127 | deferredTimeMax = deferredTime; 128 | } 129 | char deferredTimeChar[64]; 130 | snprintf(deferredTimeChar, sizeof deferredTimeChar, "%.2f", deferredTime); 131 | 132 | ImGui::Begin("Menu"); 133 | if (ImGui::CollapsingHeader("Camera")) 134 | { 135 | ImGui::Text("Position: "); 136 | ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.2f); 137 | ImGui::SameLine(); if (ImGui::InputFloat("x##pos", &(currentSettings.cameraPos.x), 0.0f, 0.0f, "%.1f", ImGuiInputTextFlags_EnterReturnsTrue)) currentSettings.updateSettings = true; 138 | ImGui::SameLine(); if (ImGui::InputFloat("y##pos", &(currentSettings.cameraPos.y), 0.0f, 0.0f, "%.1f", ImGuiInputTextFlags_EnterReturnsTrue)) currentSettings.updateSettings = true; 139 | ImGui::SameLine(); if (ImGui::InputFloat("z##pos", &(currentSettings.cameraPos.z), 0.0f, 0.0f, "%.1f", ImGuiInputTextFlags_EnterReturnsTrue)) currentSettings.updateSettings = true; 140 | ImGui::PopItemWidth(); 141 | ImGui::Separator(); 142 | ImGui::Text("Rotation: "); 143 | ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.2f); 144 | ImGui::SameLine(); if (ImGui::InputFloat("x##rot", &(currentSettings.cameraRot.x), 0.0f, 0.0f, "%.1f", ImGuiInputTextFlags_EnterReturnsTrue)) currentSettings.updateSettings = true; 145 | ImGui::SameLine(); if (ImGui::InputFloat("y##rot", &(currentSettings.cameraRot.y), 0.0f, 0.0f, "%.1f", ImGuiInputTextFlags_EnterReturnsTrue)) currentSettings.updateSettings = true; 146 | ImGui::SameLine(); if (ImGui::InputFloat("z##rot", &(currentSettings.cameraRot.z), 0.0f, 0.0f, "%.1f", ImGuiInputTextFlags_EnterReturnsTrue)) currentSettings.updateSettings = true; 147 | ImGui::PopItemWidth(); 148 | } 149 | if (ImGui::CollapsingHeader("Light")) 150 | { 151 | ImGui::Text("Direction: "); 152 | ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.2f); 153 | ImGui::SameLine(); if (ImGui::InputFloat("x##dir", &(currentSettings.lightDirection.x), 0.0f, 0.0f, "%.1f", ImGuiInputTextFlags_EnterReturnsTrue)) currentSettings.updateSettings = true; 154 | ImGui::SameLine(); if (ImGui::InputFloat("y##dir", &(currentSettings.lightDirection.y), 0.0f, 0.0f, "%.1f", ImGuiInputTextFlags_EnterReturnsTrue)) currentSettings.updateSettings = true; 155 | ImGui::SameLine(); if (ImGui::InputFloat("z##dir", &(currentSettings.lightDirection.z), 0.0f, 0.0f, "%.1f", ImGuiInputTextFlags_EnterReturnsTrue)) currentSettings.updateSettings = true; 156 | ImGui::PopItemWidth(); 157 | ImGui::Separator(); 158 | ImGui::Text("Diffuse Colour: "); 159 | if (ImGui::ColorPicker4("##picker", (float*)&(currentSettings.lightDiffuse), ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview)) currentSettings.updateSettings = true; 160 | ImGui::SameLine(); 161 | ImGui::BeginGroup(); 162 | ImGui::Text("Current"); 163 | ImGui::ColorButton("##current", ImVec4(currentSettings.lightDiffuse.x, currentSettings.lightDiffuse.y, currentSettings.lightDiffuse.z, currentSettings.lightDiffuse.w), ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40)); 164 | ImGui::EndGroup(); 165 | ImGui::Separator(); 166 | ImGui::Text("Ambient Colour: "); 167 | if (ImGui::ColorPicker4("##pickeramb", (float*)&(currentSettings.lightAmbient), ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview)) currentSettings.updateSettings = true; 168 | ImGui::SameLine(); 169 | ImGui::BeginGroup(); 170 | ImGui::Text("Current"); 171 | ImGui::ColorButton("##currentamb", ImVec4(currentSettings.lightAmbient.x, currentSettings.lightAmbient.y, currentSettings.lightAmbient.z, currentSettings.lightAmbient.w), ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40)); 172 | ImGui::EndGroup(); 173 | } 174 | if (ImGui::CollapsingHeader("Pipelines"), ImGuiTreeNodeFlags_DefaultOpen) 175 | { 176 | ImGui::Text("Switch Pipeline"); 177 | if (ImGui::Button("Visibility Buffer", ImVec2(150, 20))) 178 | { 179 | if (currentSettings.pipeline == VB_TESSELLATION) 180 | { 181 | currentSettings.pipeline = VISIBILITYBUFFER; 182 | currentSettings.updateSettings = true; 183 | } 184 | } 185 | ImGui::SameLine(); 186 | if (ImGui::Button("VB + Tessellation", ImVec2(150, 20))) 187 | { 188 | if (currentSettings.pipeline == VISIBILITYBUFFER) 189 | { 190 | currentSettings.pipeline = VB_TESSELLATION; 191 | currentSettings.updateSettings = true; 192 | } 193 | } 194 | ImGui::Text(currentSettings.pipeline == VISIBILITYBUFFER ? "Current: Visibility Buffer" : "Current: Vis Buff + Tessellation"); 195 | } 196 | if (ImGui::CollapsingHeader("Render Settings"), ImGuiTreeNodeFlags_DefaultOpen) 197 | { 198 | if (ImGui::Checkbox("Show Visibility Buffer", &(currentSettings.showVisBuff))) currentSettings.updateSettings = true; 199 | if (ImGui::Checkbox("Show Interpolated UV Coords", &(currentSettings.showInterpTex))) currentSettings.updateSettings = true; 200 | if (currentSettings.pipeline == VB_TESSELLATION) if(ImGui::Checkbox("Show Tess Coords Buffer", &(currentSettings.showTessBuff))) currentSettings.updateSettings = true; 201 | /*if (ImGui::Checkbox("Wireframe", &(currentSettings.wireframe))) currentSettings.updateSettings = true;*/ 202 | if (currentSettings.pipeline == VB_TESSELLATION) if(ImGui::SliderInt("Tess Factor", &(currentSettings.tessellationFactor), 2, 64)) currentSettings.updateSettings = true; 203 | } 204 | ImGui::End(); 205 | 206 | ImGui::Begin("Statistics"); 207 | if (ImGui::CollapsingHeader("Pass Times"), ImGuiTreeNodeFlags_DefaultOpen) 208 | { 209 | ImGui::Text("Frame Times (ms)"); 210 | ImGui::PlotHistogram("", &frameTimes[0], 50, 0, frameTimeChar, frameTimeMin, frameTimeMax, ImVec2(0, 100)); 211 | ImGui::Separator(); 212 | 213 | ImGui::Text("Forward Times (ms)"); 214 | ImGui::PlotHistogram("", &forwardTimes[0], 50, 0, forwardTimeChar, forwardTimeMin, forwardTimeMax, ImVec2(0, 100)); 215 | ImGui::Separator(); 216 | 217 | ImGui::Text("Deferred Times (ms)"); 218 | ImGui::PlotHistogram("", &deferredTimes[0], 50, 0, deferredTimeChar, deferredTimeMin, deferredTimeMax, ImVec2(0, 100)); 219 | ImGui::Separator(); 220 | 221 | if (ImGui::Button("Sample Times", ImVec2(150, 20))) 222 | { 223 | SampleFrameTimes(); 224 | SampleForwardTimes(); 225 | SampleDeferredTimes(); 226 | } 227 | if (frameTimeSample > 0.0f && forwardTimeSample > 0.0f && deferredTimeSample > 0.0f) 228 | { 229 | ImGui::Text("Averaged Time Samples (Last 20 frames)"); 230 | ImGui::Text("Full Frame: %.3f ms", frameTimeSample); 231 | ImGui::Text("Forward Pass: %.3f ms", forwardTimeSample); 232 | ImGui::Text("Deferred Pass: %.3f ms", deferredTimeSample); 233 | } 234 | 235 | ImGui::Separator(); 236 | 237 | if (ImGui::Button("Reset Times", ImVec2(150, 20))) 238 | { 239 | ResetTimeGraphs(); 240 | } 241 | } 242 | if (ImGui::CollapsingHeader("Triangle Counts"), ImGuiTreeNodeFlags_DefaultOpen) 243 | { 244 | ImGui::Text("Visibility Buffer Triangle Count: %d", visBuffTriCount); 245 | ImGui::Text("Tessellated Triangle Count: %d", tessCount); 246 | } 247 | ImGui::End(); 248 | ImGui::Render(); 249 | 250 | // Only update the application when a value has been changed 251 | if (currentSettings.updateSettings) 252 | { 253 | #if IMGUI_ENABLED 254 | appHandle->ApplySettings(currentSettings); 255 | #endif 256 | currentSettings.updateSettings = false; 257 | } 258 | } 259 | 260 | void ImGUI::DrawFrame(VkCommandBuffer commandBuffer) 261 | { 262 | ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commandBuffer); 263 | } 264 | 265 | void ImGUI::ResetTimeGraphs() 266 | { 267 | frameTimes.fill(0.0f); 268 | frameTimeMin = 9999.0f; 269 | frameTimeMax = 0.0f; 270 | forwardTimes.fill(0.0f); 271 | forwardTimeMin = 9999.0f; 272 | forwardTimeMax = 0.0f; 273 | deferredTimes.fill(0.0f); 274 | deferredTimeMin = 9999.0f; 275 | deferredTimeMax = 0.0f; 276 | } 277 | 278 | // Takes last 20 frame times and averages them 279 | void ImGUI::SampleFrameTimes() 280 | { 281 | if (frameTimes[49] != 0.0f) 282 | { 283 | float sum = 0.0f; 284 | for (int i = frameTimes.size() - 1; i > frameTimes.size() - 21; i--) 285 | { 286 | sum += frameTimes[i]; 287 | } 288 | frameTimeSample = sum / 20; 289 | } 290 | } 291 | void ImGUI::SampleForwardTimes() 292 | { 293 | if (forwardTimes[49] != 0.0f) 294 | { 295 | float sum = 0.0f; 296 | for (int i = forwardTimes.size() - 1; i > forwardTimes.size() - 21; i--) 297 | { 298 | sum += forwardTimes[i]; 299 | } 300 | forwardTimeSample = sum / 20; 301 | } 302 | } 303 | void ImGUI::SampleDeferredTimes() 304 | { 305 | if (deferredTimes[49] != 0.0f) 306 | { 307 | float sum = 0.0f; 308 | for (int i = deferredTimes.size() - 1; i > deferredTimes.size() - 21; i--) 309 | { 310 | sum += deferredTimes[i]; 311 | } 312 | deferredTimeSample = sum / 20; 313 | } 314 | } 315 | 316 | void ImGUI::CleanUp() 317 | { 318 | vkDestroyDescriptorPool(appHandle->GetVulkanCore()->Device(), descriptorPool, nullptr); 319 | ImGui_ImplVulkan_Shutdown(); 320 | ImGui_ImplGlfw_Shutdown(); 321 | ImGui::DestroyContext(); 322 | } 323 | } -------------------------------------------------------------------------------- /VisibilityBufferTessellation/VisibilityBufferTessellation.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {B3A9CFF5-C647-4253-AD27-C40BE345D2D8} 24 | Win32Proj 25 | VisibilityBufferTessellation 26 | 10.0.16299.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | Disabled 89 | true 90 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 91 | true 92 | D:\Documents\Visual Studio Projects\Libraries\imgui-master\examples;D:\Documents\Visual Studio Projects\Libraries\imgui-master;D:\Documents\Visual Studio Projects\Libraries\glfw-3.2.1.bin.WIN64\include;D:\Documents\Visual Studio Projects\Libraries\glm;D:\Documents\Visual Studio Projects\Libraries\tinyobjloader-master;D:\Documents\Visual Studio Projects\Libraries\stb-master;D:\Vulkan\1.1.85.0\Include;%(AdditionalIncludeDirectories) 93 | stdcpp17 94 | 95 | 96 | true 97 | Console 98 | D:\Vulkan\1.1.85.0\Lib;D:\Documents\Visual Studio Projects\Libraries\glfw-3.2.1.bin.WIN64\lib-vc2015;%(AdditionalLibraryDirectories) 99 | vulkan-1.lib;glfw3.lib;%(AdditionalDependencies) 100 | 101 | 102 | 103 | 104 | Level3 105 | Disabled 106 | true 107 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 108 | true 109 | D:\Repositories\VisualStudioProjects\Libraries\imgui-master\examples;D:\Repositories\VisualStudioProjects\Libraries\imgui-master;D:\Repositories\VisualStudioProjects\Libraries\glfw-3.2.1.bin.WIN64\include;D:\Repositories\VisualStudioProjects\Libraries\glm;D:\Repositories\VisualStudioProjects\Libraries\tinyobjloader-master;D:\Repositories\VisualStudioProjects\Libraries\stb-master;D:\VulkanSDK\1.1.101.0\Include;%(AdditionalIncludeDirectories) 110 | stdcpp17 111 | 112 | 113 | true 114 | Console 115 | D:\VulkanSDK\1.1.101.0\Lib;D:\Repositories\VisualStudioProjects\Libraries\glfw-3.2.1.bin.WIN64\lib-vc2015;%(AdditionalLibraryDirectories) 116 | vulkan-1.lib;glfw3.lib;%(AdditionalDependencies) 117 | 118 | 119 | 120 | 121 | Level3 122 | MaxSpeed 123 | true 124 | true 125 | true 126 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 127 | true 128 | D:\Documents\Visual Studio Projects\Libraries\imgui-master\examples;D:\Documents\Visual Studio Projects\Libraries\imgui-master;D:\Documents\Visual Studio Projects\Libraries\glfw-3.2.1.bin.WIN64\include;D:\Documents\Visual Studio Projects\Libraries\glm;D:\Documents\Visual Studio Projects\Libraries\tinyobjloader-master;D:\Documents\Visual Studio Projects\Libraries\stb-master;D:\Vulkan\1.1.85.0\Include;%(AdditionalIncludeDirectories) 129 | stdcpp17 130 | 131 | 132 | true 133 | true 134 | true 135 | Console 136 | D:\Vulkan\1.1.85.0\Lib;D:\Documents\Visual Studio Projects\Libraries\glfw-3.2.1.bin.WIN64\lib-vc2015;%(AdditionalLibraryDirectories) 137 | vulkan-1.lib;glfw3.lib;%(AdditionalDependencies) 138 | 139 | 140 | 141 | 142 | Level3 143 | MaxSpeed 144 | true 145 | true 146 | true 147 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 148 | true 149 | D:\Repositories\VisualStudioProjects\Libraries\imgui-master\examples;D:\Repositories\VisualStudioProjects\Libraries\imgui-master;D:\Repositories\VisualStudioProjects\Libraries\glfw-3.2.1.bin.WIN64\include;D:\Repositories\VisualStudioProjects\Libraries\glm;D:\Repositories\VisualStudioProjects\Libraries\tinyobjloader-master;D:\Repositories\VisualStudioProjects\Libraries\stb-master;D:\VulkanSDK\1.1.101.0\Include;%(AdditionalIncludeDirectories) 150 | stdcpp17 151 | 152 | 153 | true 154 | true 155 | true 156 | Console 157 | D:\VulkanSDK\1.1.101.0\Lib;D:\Repositories\VisualStudioProjects\Libraries\glfw-3.2.1.bin.WIN64\lib-vc2015;%(AdditionalLibraryDirectories) 158 | vulkan-1.lib;glfw3.lib;%(AdditionalDependencies) 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | false 210 | false 211 | 212 | 213 | false 214 | false 215 | 216 | 217 | false 218 | false 219 | 220 | 221 | 222 | false 223 | false 224 | 225 | 226 | false 227 | false 228 | 229 | 230 | false 231 | false 232 | 233 | 234 | false 235 | false 236 | 237 | 238 | false 239 | false 240 | 241 | 242 | false 243 | false 244 | 245 | 246 | false 247 | false 248 | 249 | 250 | false 251 | false 252 | 253 | 254 | false 255 | false 256 | 257 | 258 | 259 | 260 | 261 | --------------------------------------------------------------------------------