diff --git a/Vulkan/Vulkan.vcxproj b/Vulkan/Vulkan.vcxproj index eb0166f..6d5e667 100644 --- a/Vulkan/Vulkan.vcxproj +++ b/Vulkan/Vulkan.vcxproj @@ -168,11 +168,13 @@ + + - + diff --git a/Vulkan/Vulkan.vcxproj.filters b/Vulkan/Vulkan.vcxproj.filters index e63f622..eeb7862 100644 --- a/Vulkan/Vulkan.vcxproj.filters +++ b/Vulkan/Vulkan.vcxproj.filters @@ -74,7 +74,13 @@ Header Files - + + Header Files + + + Header Files + + Header Files diff --git a/Vulkan/compile.bat b/Vulkan/compile.bat new file mode 100644 index 0000000..0741ede --- /dev/null +++ b/Vulkan/compile.bat @@ -0,0 +1,3 @@ +glslc res/shaders/shader.vert -o res/shaders/vert.spv +glslc res/shaders/shader.frag -o res/shaders/frag.spv +pause \ No newline at end of file diff --git a/Vulkan/res/shaders/frag.spv b/Vulkan/res/shaders/frag.spv index da37f7e..841eb58 100644 Binary files a/Vulkan/res/shaders/frag.spv and b/Vulkan/res/shaders/frag.spv differ diff --git a/Vulkan/res/shaders/shader.frag b/Vulkan/res/shaders/shader.frag index 7122ce8..500463e 100644 --- a/Vulkan/res/shaders/shader.frag +++ b/Vulkan/res/shaders/shader.frag @@ -1,8 +1,12 @@ #version 450 +layout(set = 1, binding = 0) uniform sampler2D texSampler; + layout(location = 0) in vec3 fragColor; +layout(location = 1) in vec2 fragTexCoord; + layout(location = 0) out vec4 outColor; void main() { - outColor = vec4(fragColor, 1.0); + outColor = vec4(fragColor, 1.0) * texture(texSampler, fragTexCoord); } diff --git a/Vulkan/res/shaders/shader.vert b/Vulkan/res/shaders/shader.vert index 00a3fb8..a537cc4 100644 --- a/Vulkan/res/shaders/shader.vert +++ b/Vulkan/res/shaders/shader.vert @@ -10,10 +10,13 @@ layout(set = 0, binding = 0) uniform SceneUniformBufferObject layout(location = 0) in vec2 inPosition; layout(location = 1) in vec3 inColor; +layout(location = 2) in vec2 inTexCoord; layout(location = 0) out vec3 fragColor; +layout(location = 1) out vec2 fragTexCoord; void main() { gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPosition.x, 0.0, inPosition.y, 1.0); fragColor = inColor; + fragTexCoord = inTexCoord; } \ No newline at end of file diff --git a/Vulkan/res/shaders/vert.spv b/Vulkan/res/shaders/vert.spv index f7d1531..5fadf46 100644 Binary files a/Vulkan/res/shaders/vert.spv and b/Vulkan/res/shaders/vert.spv differ diff --git a/Vulkan/res/textures/texture.png b/Vulkan/res/textures/texture.png new file mode 100644 index 0000000..8d45e99 Binary files /dev/null and b/Vulkan/res/textures/texture.png differ diff --git a/Vulkan/src/Buffer.h b/Vulkan/src/Buffer.h index a39c304..9298bb8 100644 --- a/Vulkan/src/Buffer.h +++ b/Vulkan/src/Buffer.h @@ -36,7 +36,7 @@ public: VkMemoryAllocateInfo allocateInfo{}; allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocateInfo.allocationSize = memoryRequirements.size; - allocateInfo.memoryTypeIndex = FindMemoryType(instance, memoryRequirements.memoryTypeBits, properties); + allocateInfo.memoryTypeIndex = instance.FindMemoryType(memoryRequirements.memoryTypeBits, properties); CP_VK_ASSERT(vkAllocateMemory(instance.GetDevice(), &allocateInfo, nullptr, &memory), "Failed to allocate buffer memory"); @@ -85,10 +85,11 @@ public: CopyBuffer(instance, stagingBuffer, *this, offset, size); } - void Map() + void* Map() { CP_ASSERT(mappedData == nullptr, "Mapping an already mapped buffer"); vkMapMemory(instance.GetDevice(), memory, 0, size * count, 0, &mappedData); + return mappedData; } void Unmap() @@ -166,17 +167,4 @@ public: vkFreeCommandBuffers(instance.GetDevice(), instance.GetCommandPool(), 1, &commandBuffer); } - -private: - static uint32_t FindMemoryType(Instance& instance, uint32_t typeFilter, VkMemoryPropertyFlags properties) - { - VkPhysicalDeviceMemoryProperties memoryProperties; - vkGetPhysicalDeviceMemoryProperties(instance.GetPhysicalDevice(), &memoryProperties); - for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; ++i) - { - if ((typeFilter & (1 << i)) && (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) - return i; - } - throw std::runtime_error("Failed to find suitable memory type"); - } }; diff --git a/Vulkan/src/DescriptorPool.h b/Vulkan/src/DescriptorPool.h new file mode 100644 index 0000000..0d7438f --- /dev/null +++ b/Vulkan/src/DescriptorPool.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Common.h" +#include "Instance.h" +#include + +class DescriptorPool final +{ + CP_DELETE_COPY_AND_MOVE_CTOR(DescriptorPool); +private: + Instance& instance; + + VkDescriptorPool descriptorPool; + static const int DESCRIPTOR_SET_COUNT = 100; +public: + DescriptorPool(Instance& instance) + : instance{instance} + { + std::vector poolSizes{2}; + poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSizes[0].descriptorCount = DESCRIPTOR_SET_COUNT * instance.GetMaxFramesInFlight(); // TODO: how should this actually be determined? + + poolSizes[1].type = VK_DESCRIPTOR_TYPE_SAMPLER; + poolSizes[1].descriptorCount = DESCRIPTOR_SET_COUNT * instance.GetMaxFramesInFlight(); // TODO: how should this actually be determined? + + VkDescriptorPoolCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + createInfo.poolSizeCount = poolSizes.size(); + createInfo.pPoolSizes = poolSizes.data(); + createInfo.maxSets = DESCRIPTOR_SET_COUNT * instance.GetMaxFramesInFlight(); + + CP_VK_ASSERT(vkCreateDescriptorPool(instance.GetDevice(), &createInfo, nullptr, &descriptorPool), "Failed to initialize descriptor pool"); + } + + ~DescriptorPool() + { + vkDestroyDescriptorPool(instance.GetDevice(), descriptorPool, nullptr); + } + + std::vector AllocateDescriptorSets(VkDescriptorSetLayout descriptorSetLayout) + { + std::vector descriptorSets; + std::vector layouts{static_cast(instance.GetMaxFramesInFlight()), descriptorSetLayout}; + VkDescriptorSetAllocateInfo allocateInfo{}; + allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocateInfo.descriptorPool = descriptorPool; + allocateInfo.descriptorSetCount = instance.GetMaxFramesInFlight(); + allocateInfo.pSetLayouts = layouts.data(); + + descriptorSets.resize(instance.GetMaxFramesInFlight()); + CP_VK_ASSERT(vkAllocateDescriptorSets(instance.GetDevice(), &allocateInfo, descriptorSets.data()), "Failed to allocate descriptor sets"); + + return descriptorSets; + } +}; diff --git a/Vulkan/src/DescriptorSet.h b/Vulkan/src/DescriptorSet.h new file mode 100644 index 0000000..5c23fe5 --- /dev/null +++ b/Vulkan/src/DescriptorSet.h @@ -0,0 +1,67 @@ +#pragma once + +#include "Common.h" +#include "DescriptorPool.h" +#include "Sampler.h" +#include "UniformBuffer.h" +#include + +class DescriptorSet +{ + CP_DELETE_COPY_AND_MOVE_CTOR(DescriptorSet); +private: + Instance& instance; + DescriptorPool& descriptorPool; + VkDescriptorSetLayout descriptorSetLayout; + + std::vector descriptorSets; + +public: + DescriptorSet(Instance& instance, DescriptorPool& descriptorPool, VkDescriptorSetLayout descriptorSetLayout) + : instance{instance}, descriptorPool{descriptorPool}, descriptorSetLayout{descriptorSetLayout} + { + descriptorSets = descriptorPool.AllocateDescriptorSets(descriptorSetLayout); + } + + void AddUniform(const UniformBuffer& uniformBuffer, uint32_t binding) + { + for (size_t i = 0; i < instance.GetMaxFramesInFlight(); ++i) { + VkDescriptorBufferInfo bufferInfo = uniformBuffer.GetDescriptorBufferInfo(i); + + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSets[i]; + descriptorWrite.dstBinding = binding; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &bufferInfo; + descriptorWrite.pImageInfo = nullptr; + descriptorWrite.pTexelBufferView = nullptr; + vkUpdateDescriptorSets(instance.GetDevice(), 1, &descriptorWrite, 0, nullptr); + } + } + + void AddSampler(const Sampler& sampler, uint32_t binding) + { + VkDescriptorImageInfo imageInfo = sampler.GetDescriptorImageInfo(); + for (auto&& descriptorSet : descriptorSets) { + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSet; + descriptorWrite.dstBinding = binding; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = nullptr; + descriptorWrite.pImageInfo = &imageInfo; + descriptorWrite.pTexelBufferView = nullptr; + vkUpdateDescriptorSets(instance.GetDevice(), 1, &descriptorWrite, 0, nullptr); + } + } + + VkDescriptorSet GetHandle() const + { + return descriptorSets[instance.GetFlightIndex()]; + } +}; \ No newline at end of file diff --git a/Vulkan/src/Instance.h b/Vulkan/src/Instance.h index b2847c0..855e58a 100644 --- a/Vulkan/src/Instance.h +++ b/Vulkan/src/Instance.h @@ -157,6 +157,19 @@ public: return *swapChain; } + // TODO: Create Device class and move this there + uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) + { + VkPhysicalDeviceMemoryProperties memoryProperties; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; ++i) + { + if ((typeFilter & (1 << i)) && (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + return i; + } + throw std::runtime_error("Failed to find suitable memory type"); + } + private: void InitializeWindow(const std::string& applicationName) { @@ -277,6 +290,7 @@ private: std::vector deviceExtensions = GetRequiredDeviceExtensions(); VkPhysicalDeviceFeatures deviceFeatures{}; deviceFeatures.fillModeNonSolid = VK_TRUE; + deviceFeatures.samplerAnisotropy = VK_TRUE; VkDeviceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.pQueueCreateInfos = queueCreateInfos.data(); @@ -380,7 +394,7 @@ private: VkPhysicalDeviceFeatures deviceFeatures; vkGetPhysicalDeviceFeatures(device, &deviceFeatures); - if (!deviceFeatures.fillModeNonSolid) + if (!deviceFeatures.fillModeNonSolid || !deviceFeatures.samplerAnisotropy) return false; QueueFamiliesQuery query{surface, device}; diff --git a/Vulkan/src/Pipeline.h b/Vulkan/src/Pipeline.h index a68228f..f6d9238 100644 --- a/Vulkan/src/Pipeline.h +++ b/Vulkan/src/Pipeline.h @@ -15,8 +15,7 @@ class Pipeline private: Instance& instance; - VkDescriptorSetLayout vertexDescriptorSetLayout; - VkDescriptorSetLayout fragmentDescriptorSetLayout; + std::vector descriptorSetLayouts{}; VkPipelineLayout pipelineLayout; VkPipeline graphicsPipeline; @@ -24,7 +23,7 @@ public: Pipeline(Instance& instance, PipelineCreator creator) : instance{instance} { - InitializeDescriptorSetLayouts(creator); + InitializeDescriptorSetLayout(creator); InitializePipeline(creator); } @@ -32,8 +31,10 @@ public: { vkDestroyPipeline(instance.GetDevice(), graphicsPipeline, nullptr); vkDestroyPipelineLayout(instance.GetDevice(), pipelineLayout, nullptr); - vkDestroyDescriptorSetLayout(instance.GetDevice(), vertexDescriptorSetLayout, nullptr); - vkDestroyDescriptorSetLayout(instance.GetDevice(), fragmentDescriptorSetLayout, nullptr); + for (auto&& descriptorSetLayout : descriptorSetLayouts) + { + vkDestroyDescriptorSetLayout(instance.GetDevice(), descriptorSetLayout, nullptr); + } } void Bind(VkCommandBuffer commandBuffer) @@ -59,21 +60,32 @@ public: return pipelineLayout; } - VkDescriptorSetLayout GetVertexDescriptorSetLayout() + VkDescriptorSetLayout GetDescriptorSetLayout(uint32_t setIndex) const { - return vertexDescriptorSetLayout; - } - - VkDescriptorSetLayout GetFragmentDescriptorSetLayout() - { - return fragmentDescriptorSetLayout; + return descriptorSetLayouts[setIndex]; } private: - void InitializeDescriptorSetLayouts(const PipelineCreator& creator) + void InitializeDescriptorSetLayout(const PipelineCreator& creator) { - vertexDescriptorSetLayout = InitializeDescriptorSetLayouts(creator.vertexDescriptorSetLayouts, VK_SHADER_STAGE_VERTEX_BIT); - fragmentDescriptorSetLayout = InitializeDescriptorSetLayouts(creator.fragmentDescriptorSetLayouts, VK_SHADER_STAGE_FRAGMENT_BIT); + descriptorSetLayouts.resize(creator.descriptorSetLayouts.size()); + int i = 0; + for (auto&& binding : creator.descriptorSetLayouts) + { + VkDescriptorSetLayoutBinding layoutBinding{}; + layoutBinding.binding = 0; + layoutBinding.descriptorType = binding.second.type; + layoutBinding.descriptorCount = 1; + layoutBinding.stageFlags = binding.second.flags; + layoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + createInfo.bindingCount = 1; + createInfo.pBindings = &layoutBinding; + + CP_VK_ASSERT(vkCreateDescriptorSetLayout(instance.GetDevice(), &createInfo, nullptr, &descriptorSetLayouts[i++]), "Failed to initialize descriptor set layout"); + } } void InitializePipeline(const PipelineCreator& creator) @@ -185,13 +197,10 @@ private: colorBlendCreateInfo.blendConstants[2] = 0.0f; colorBlendCreateInfo.blendConstants[3] = 0.0f; - std::vector layouts{}; - if (vertexDescriptorSetLayout != VK_NULL_HANDLE) layouts.emplace_back(vertexDescriptorSetLayout); - if (fragmentDescriptorSetLayout != VK_NULL_HANDLE) layouts.emplace_back(fragmentDescriptorSetLayout ); VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{}; pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutCreateInfo.setLayoutCount = layouts.size(); - pipelineLayoutCreateInfo.pSetLayouts = layouts.data(); + pipelineLayoutCreateInfo.setLayoutCount = descriptorSetLayouts.size(); + pipelineLayoutCreateInfo.pSetLayouts = descriptorSetLayouts.data(); pipelineLayoutCreateInfo.pushConstantRangeCount = 0; pipelineLayoutCreateInfo.pPushConstantRanges = nullptr; @@ -234,37 +243,4 @@ private: return shaderModule; } - VkDescriptorSetLayout InitializeDescriptorSetLayouts(const std::set& bindings, VkShaderStageFlags flags) - { - VkDescriptorSetLayout descriptorSetLayout; - std::vector descriptorSetLayoutBindings{bindings.size()}; - - int i = 0; - for (auto&& binding : bindings) - { - VkDescriptorSetLayoutBinding layoutBinding{}; - layoutBinding.binding = binding; - layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - layoutBinding.descriptorCount = 1; - layoutBinding.stageFlags = flags; - layoutBinding.pImmutableSamplers = nullptr; - descriptorSetLayoutBindings[i++] = layoutBinding; - } - - if (!descriptorSetLayoutBindings.empty()) - { - VkDescriptorSetLayoutCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - createInfo.bindingCount = descriptorSetLayoutBindings.size(); - createInfo.pBindings = descriptorSetLayoutBindings.data(); - - CP_VK_ASSERT(vkCreateDescriptorSetLayout(instance.GetDevice(), &createInfo, nullptr, &descriptorSetLayout), "Failed to initialize descriptor set layout"); - - return descriptorSetLayout; - } - else - { - return VK_NULL_HANDLE; - } - } }; \ No newline at end of file diff --git a/Vulkan/src/PipelineCreator.h b/Vulkan/src/PipelineCreator.h index 9acfd3f..63553c0 100644 --- a/Vulkan/src/PipelineCreator.h +++ b/Vulkan/src/PipelineCreator.h @@ -6,10 +6,14 @@ class PipelineCreator { + struct DescriptorSetLayout + { + VkDescriptorType type; + VkShaderStageFlags flags; + }; friend class Pipeline; private: - std::set vertexDescriptorSetLayouts{}; - std::set fragmentDescriptorSetLayouts{}; + std::map descriptorSetLayouts{}; std::string vertexShader; std::string fragmentShader; @@ -29,15 +33,9 @@ public: vertexDescriptor = descriptor; } - void AddVertexDescriptorSetLayoutBinding(uint32_t binding) + void AddDescriptorSetLayoutBinding(uint32_t set, VkDescriptorType type, VkShaderStageFlags stageFlags) { - CP_ASSERT(binding == 0, "Currently only support uniforms with binding = 0"); - vertexDescriptorSetLayouts.emplace(binding); - } - - void AddFragmentDescriptorSetLayoutBinding(uint32_t binding) - { - fragmentDescriptorSetLayouts.emplace(binding); + descriptorSetLayouts.emplace(set, DescriptorSetLayout{type, stageFlags}); } void SetPrimitiveTopology(VkPrimitiveTopology primitiveTopology) diff --git a/Vulkan/src/Sampler.h b/Vulkan/src/Sampler.h new file mode 100644 index 0000000..58849ff --- /dev/null +++ b/Vulkan/src/Sampler.h @@ -0,0 +1,61 @@ +#pragma once + +#include "Common.h" +#include "Instance.h" +#include "Pipeline.h" + +class Sampler +{ + CP_DELETE_COPY_AND_MOVE_CTOR(Sampler); +private: + Instance& instance; + VkImageView imageView; + + VkSampler textureSampler; +public: + Sampler(Instance& instance, VkImageView imageView) + : instance{instance}, imageView{imageView} + { + InitializeSampler(imageView); + } + + ~Sampler() + { + vkDestroySampler(instance.GetDevice(), textureSampler, nullptr); + } + + VkDescriptorImageInfo GetDescriptorImageInfo() const + { + VkDescriptorImageInfo imageInfo{}; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfo.imageView = imageView; + imageInfo.sampler = textureSampler; + + return imageInfo; + } +private: + void InitializeSampler(VkImageView imageView) + { + VkPhysicalDeviceProperties properties{}; + vkGetPhysicalDeviceProperties(instance.GetPhysicalDevice(), &properties); + + VkSamplerCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + createInfo.magFilter = VK_FILTER_LINEAR; + createInfo.minFilter = VK_FILTER_LINEAR; + createInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + createInfo.anisotropyEnable = VK_TRUE; + createInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy; + createInfo.unnormalizedCoordinates = VK_FALSE; + createInfo.compareEnable = VK_FALSE; + createInfo.compareOp = VK_COMPARE_OP_ALWAYS; + createInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + createInfo.mipLodBias = 0.0f; + createInfo.minLod = 0.0f; + createInfo.maxLod = 0.0f; + + CP_VK_ASSERT(vkCreateSampler(instance.GetDevice(), &createInfo, nullptr, &textureSampler), "Failed to initialize texture sampler"); + } +}; diff --git a/Vulkan/src/UniformBuffer.h b/Vulkan/src/UniformBuffer.h index bd75947..3853b06 100644 --- a/Vulkan/src/UniformBuffer.h +++ b/Vulkan/src/UniformBuffer.h @@ -5,84 +5,28 @@ #include "Pipeline.h" #include -template class UniformBuffer : public Buffer { CP_DELETE_COPY_AND_MOVE_CTOR(UniformBuffer); -private: - Pipeline& pipeline; - - VkDescriptorPool descriptorPool; - std::vector descriptorSets; public: - UniformBuffer(Instance& instance, Pipeline& pipeline, uint32_t binding, VkDescriptorSetLayout layout) - : Buffer{instance, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(T), instance.GetMaxFramesInFlight()}, pipeline{pipeline} - { - InitializeDescriptorPool(); - InitializeDescriptorSet(binding, layout); - } - - ~UniformBuffer() override - { - vkDestroyDescriptorPool(instance.GetDevice(), descriptorPool, nullptr); - } + UniformBuffer(Instance& instance, VkDeviceSize size) + : Buffer{instance, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size, instance.GetMaxFramesInFlight()} + {} + template void Update(const T& t) { + CP_ASSERT(sizeof(T) == Buffer::GetSize(), "Template size is not the same as buffer size %u != %u", sizeof(T), Buffer::GetSize()); Buffer::Update((void*)&t, instance.GetFlightIndex()); } void Bind(VkCommandBuffer commandBuffer) const { - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.GetPipelineLayout(), 0, 1, &descriptorSets[instance.GetFlightIndex()], 0, nullptr); + // vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.GetPipelineLayout(), 0, 1, &descriptorSets[instance.GetFlightIndex()], 0, nullptr); } -private: - void InitializeDescriptorPool() - { - VkDescriptorPoolSize poolSize{}; - poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - poolSize.descriptorCount = instance.GetMaxFramesInFlight(); - - VkDescriptorPoolCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - createInfo.poolSizeCount = 1; - createInfo.pPoolSizes = &poolSize; - createInfo.maxSets = instance.GetMaxFramesInFlight(); - - CP_VK_ASSERT(vkCreateDescriptorPool(instance.GetDevice(), &createInfo, nullptr, &descriptorPool), "Failed to initialize descriptor pool"); - } - - void InitializeDescriptorSet(uint32_t binding, VkDescriptorSetLayout layout) - { - std::vector layouts{static_cast(instance.GetMaxFramesInFlight()), layout}; - VkDescriptorSetAllocateInfo allocateInfo{}; - allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - allocateInfo.descriptorPool = descriptorPool; - allocateInfo.descriptorSetCount = instance.GetMaxFramesInFlight(); - allocateInfo.pSetLayouts = layouts.data(); - - descriptorSets.resize(instance.GetMaxFramesInFlight()); - CP_VK_ASSERT(vkAllocateDescriptorSets(instance.GetDevice(), &allocateInfo, descriptorSets.data()), "Failed to allocate descriptor sets"); - for (size_t i = 0; i < instance.GetMaxFramesInFlight(); ++i) { - VkDescriptorBufferInfo bufferInfo = GetDescriptorBufferInfo(i); - - VkWriteDescriptorSet descriptorWrite{}; - descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrite.dstSet = descriptorSets[i]; - descriptorWrite.dstBinding = binding; - descriptorWrite.dstArrayElement = 0; - descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrite.descriptorCount = 1; - descriptorWrite.pBufferInfo = &bufferInfo; - descriptorWrite.pImageInfo = nullptr; - descriptorWrite.pTexelBufferView = nullptr; - vkUpdateDescriptorSets(instance.GetDevice(), 1, &descriptorWrite, 0, nullptr); - } - } - - VkDescriptorBufferInfo GetDescriptorBufferInfo(int index) + VkDescriptorBufferInfo GetDescriptorBufferInfo(int index) const { VkDescriptorBufferInfo bufferInfo{}; bufferInfo.buffer = handle; diff --git a/Vulkan/src/Vertex.h b/Vulkan/src/Vertex.h index 852b609..9700f2f 100644 --- a/Vulkan/src/Vertex.h +++ b/Vulkan/src/Vertex.h @@ -7,41 +7,15 @@ struct Vertex { glm::vec2 pos; glm::vec3 color; + glm::vec2 texCoord; static VertexDescriptor GetDescriptor() { VertexDescriptor descriptor{}; descriptor.AddAttribute(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, pos)); descriptor.AddAttribute(0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, color)); + descriptor.AddAttribute(0, 2, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, texCoord)); return descriptor; } - - static VkVertexInputBindingDescription GetBindingDescription() - { - VkVertexInputBindingDescription description{}; - - description.binding = 0; - description.stride = sizeof(Vertex); - description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - return description; - } - - static std::vector GetAttributeDescriptions() - { - std::vector descriptions{2}; - - descriptions[0].binding = 0; - descriptions[0].location = 0; - descriptions[0].format = VK_FORMAT_R32G32_SFLOAT; - descriptions[0].offset = offsetof(Vertex, pos); - - descriptions[1].binding = 0; - descriptions[1].location = 1; - descriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; - descriptions[1].offset = offsetof(Vertex, color); - - return descriptions; - } }; diff --git a/Vulkan/src/main.cpp b/Vulkan/src/main.cpp index 7cb4061..0fd310c 100644 --- a/Vulkan/src/main.cpp +++ b/Vulkan/src/main.cpp @@ -5,8 +5,11 @@ #include "Instance.h" #include "Timer.h" #include "UniformBuffer.h" +#include "Sampler.h" #include "Vertex.h" #include "Pipeline.h" +#include "DescriptorPool.h" +#include "DescriptorSet.h" #include #include @@ -14,29 +17,16 @@ #include #include #include +#define STB_IMAGE_IMPLEMENTATION #include #include #include const std::vector vertices = { - Vertex{{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}}, - Vertex{{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}}, - Vertex{{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}, - Vertex{{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}} -}; - -const std::vector positions = { - {-0.5f, -0.5f}, - {0.5f, -0.5f}, - {0.5f, 0.5f}, - {-0.5f, 0.5f} -}; - -const std::vector colors = { - glm::vec3{1.0f, 0.0f, 0.0f}, - glm::vec3{0.0f, 1.0f, 0.0f}, - glm::vec3{0.0f, 0.0f, 1.0f}, - glm::vec3{1.0f, 1.0f, 1.0f} + Vertex{{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + Vertex{{ 0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, + Vertex{{ 0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, + Vertex{{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}} }; const std::vector indices = { @@ -58,15 +48,27 @@ private: std::unique_ptr graphicsPipeline; std::unique_ptr vertexBuffer; std::unique_ptr indexBuffer; - std::unique_ptr> shaderUniformBuffer; + std::unique_ptr shaderUniformBuffer; + std::unique_ptr sampler; + std::unique_ptr descriptorPool; + std::unique_ptr uniformDescriptorSet; + std::unique_ptr samplerDescriptorSet; std::vector commandBuffers; + VkImage textureImage; + VkDeviceMemory textureImageMemory; + VkImageView textureImageView; + public: Application() { InitializeInstance(); InitializeGraphicsPipeline(); + InitializeTextureImage(); + InitializeTextureImageView(); + InitializeTextureSampler(); InitializeUniformBuffer(); + InitializeDescriptorSets(); InitializeVertexBuffer(); InitializeIndexBuffer(); InitializeCommandBuffers(); @@ -75,6 +77,9 @@ public: ~Application() { vkDeviceWaitIdle(instance->GetDevice()); + vkDestroyImage(instance->GetDevice(), textureImage, nullptr); + vkFreeMemory(instance->GetDevice(), textureImageMemory, nullptr); + vkDestroyImageView(instance->GetDevice(), textureImageView, nullptr); } Application(Application&&) = delete; @@ -100,15 +105,207 @@ private: instance = std::make_unique("Vulkan Tutorial"); } + void InitializeTextureImage() + { + int texWidth; + int texHeight; + int texChannels; + stbi_uc* pixels = stbi_load("res/textures/texture.png", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); + + CP_ASSERT(pixels, "Failed to load texture image"); + + VkDeviceSize bufferSize = texWidth * texHeight * 4; + Buffer stagingBuffer{*instance, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, bufferSize, 1}; + void* data = stagingBuffer.Map(); + memcpy(data, pixels, bufferSize); + stagingBuffer.Unmap(); + stbi_image_free(pixels); + + VkImageCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + createInfo.imageType = VK_IMAGE_TYPE_2D; + createInfo.extent.width = texWidth; + createInfo.extent.height = texHeight; + createInfo.extent.depth = 1; + createInfo.mipLevels = 1; + createInfo.arrayLayers = 1; + createInfo.format = VK_FORMAT_R8G8B8A8_SRGB; + createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + createInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.samples = VK_SAMPLE_COUNT_1_BIT; + createInfo.flags = 0; + + CP_VK_ASSERT(vkCreateImage(instance->GetDevice(), &createInfo, nullptr, &textureImage), "Failed to initialize texture image"); + + VkMemoryRequirements memoryRequirements; + vkGetImageMemoryRequirements(instance->GetDevice(), textureImage, &memoryRequirements); + + VkMemoryAllocateInfo allocateInfo{}; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.allocationSize = memoryRequirements.size; + allocateInfo.memoryTypeIndex = instance->FindMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + CP_VK_ASSERT(vkAllocateMemory(instance->GetDevice(), &allocateInfo, nullptr, &textureImageMemory), "Failed to initiallizse texture image memory"); + + vkBindImageMemory(instance->GetDevice(), textureImage, textureImageMemory, 0); + + TransitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + CopyBufferToImage(stagingBuffer, textureImage, texWidth, texHeight); + TransitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } + + void InitializeTextureImageView() + { + textureImageView = CreateImageView(); + } + + void InitializeTextureSampler() + { + sampler = std::make_unique(*instance, textureImageView); + } + + VkImageView CreateImageView() + { + VkImageView imageView; + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = textureImage; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = VK_FORMAT_R8G8B8A8_SRGB; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + CP_VK_ASSERT(vkCreateImageView(instance->GetDevice(), &createInfo, nullptr, &imageView), "Failed to initialize ImageView"); + + return imageView; + } + + void TransitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) + { + VkCommandBuffer commandBuffer = BeginSingleUseCommandBuffer(); + + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = 0; + + VkPipelineStageFlags srcStage; + VkPipelineStageFlags dstStage; + + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; + dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } else { + throw std::invalid_argument("Unsupported layout transition"); + } + + vkCmdPipelineBarrier(commandBuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); + + EndSinglelUseCommandBuffer(commandBuffer); + } + + void CopyBufferToImage(const Buffer& buffer, VkImage image, uint32_t width, uint32_t height) + { + + VkCommandBuffer commandBuffer = BeginSingleUseCommandBuffer(); + + VkBufferImageCopy region{}; + region.bufferOffset = 0; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + + region.imageOffset = {0, 0, 0}; + region.imageExtent = {width, height, 1}; + + vkCmdCopyBufferToImage(commandBuffer, buffer.GetHandle(), image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + EndSinglelUseCommandBuffer(commandBuffer); + } + + VkCommandBuffer BeginSingleUseCommandBuffer() + { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = instance->GetCommandPool(); + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(instance->GetDevice(), &allocInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + return commandBuffer; + } + + void EndSinglelUseCommandBuffer(VkCommandBuffer commandBuffer) + { + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(instance->GetGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(instance->GetGraphicsQueue()); + + vkFreeCommandBuffers(instance->GetDevice(), instance->GetCommandPool(), 1, &commandBuffer); + + } + void InitializeUniformBuffer() { - shaderUniformBuffer = std::make_unique>(*instance, *graphicsPipeline, 0, graphicsPipeline->GetVertexDescriptorSetLayout()); + shaderUniformBuffer = std::make_unique(*instance, sizeof(ShaderUniform)); + } + + void InitializeDescriptorSets() + { + descriptorPool = std::make_unique(*instance); + + uniformDescriptorSet = std::make_unique(*instance, *descriptorPool, graphicsPipeline->GetDescriptorSetLayout(0)); + uniformDescriptorSet->AddUniform(*shaderUniformBuffer, 0); + + samplerDescriptorSet = std::make_unique(*instance, *descriptorPool, graphicsPipeline->GetDescriptorSetLayout(1)); + samplerDescriptorSet->AddSampler(*sampler, 0); } void InitializeGraphicsPipeline() { PipelineCreator creator{"res/shaders/vert.spv", "res/shaders/frag.spv"}; - creator.AddVertexDescriptorSetLayoutBinding(0); + creator.AddDescriptorSetLayoutBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT); + creator.AddDescriptorSetLayoutBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT); creator.SetVertexDescriptor(Vertex::GetDescriptor()); creator.SetCullMode(VK_CULL_MODE_NONE); graphicsPipeline = std::make_unique(*instance, creator); @@ -171,6 +368,11 @@ private: indexBuffer->Bind(commandBuffer); shaderUniformBuffer->Bind(commandBuffer); + std::vector sets{uniformDescriptorSet->GetHandle(), samplerDescriptorSet->GetHandle()}; + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline->GetPipelineLayout(), 0, sets.size(), sets.data(), 0, nullptr); + // Replace the two lines above with this somehow: + // graphicsPipeline->BindDescriptorSets(uniformDescriptorSet, samplerDescriptorSet); + indexBuffer->Draw(commandBuffer); vkCmdEndRenderPass(commandBuffer);