diff --git a/Vulkan/Vulkan.vcxproj b/Vulkan/Vulkan.vcxproj index 6d5e667..8708a94 100644 --- a/Vulkan/Vulkan.vcxproj +++ b/Vulkan/Vulkan.vcxproj @@ -123,7 +123,7 @@ Console true $(ProjectDir)ext/lib/;C:/VulkanSDK/1.3.236.0/Lib;%(AdditionalLibraryDirectories) - vulkan-1.lib;glfw3.lib;%(AdditionalDependencies) + vulkan-1.lib;glfw3.lib;shaderc_combinedd.lib;%(AdditionalDependencies) @@ -150,7 +150,7 @@ true true $(ProjectDir)ext/lib/;C:/VulkanSDK/1.3.236.0/Lib;%(AdditionalLibraryDirectories) - vulkan-1.lib;glfw3.lib;%(AdditionalDependencies) + vulkan-1.lib;glfw3.lib;shaderc_combinedd.lib;%(AdditionalDependencies) @@ -166,15 +166,19 @@ + + - + + + diff --git a/Vulkan/Vulkan.vcxproj.filters b/Vulkan/Vulkan.vcxproj.filters index eeb7862..841e1ff 100644 --- a/Vulkan/Vulkan.vcxproj.filters +++ b/Vulkan/Vulkan.vcxproj.filters @@ -74,7 +74,7 @@ Header Files - + Header Files @@ -83,6 +83,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/Vulkan/res/shaders/frag.spv b/Vulkan/res/shaders/frag.spv deleted file mode 100644 index 841eb58..0000000 Binary files a/Vulkan/res/shaders/frag.spv and /dev/null differ diff --git a/Vulkan/res/shaders/shader.frag b/Vulkan/res/shaders/shader.frag index 500463e..27f1330 100644 --- a/Vulkan/res/shaders/shader.frag +++ b/Vulkan/res/shaders/shader.frag @@ -1,12 +1,15 @@ #version 450 -layout(set = 1, binding = 0) uniform sampler2D texSampler; +layout(set = 0, binding = 1) uniform sampler2D texSampler; layout(location = 0) in vec3 fragColor; layout(location = 1) in vec2 fragTexCoord; +layout(location = 2) in vec3 fragPosition; +layout(location = 3) in vec3 fragLightPos; layout(location = 0) out vec4 outColor; void main() { - outColor = vec4(fragColor, 1.0) * texture(texSampler, fragTexCoord); + float scale = 0.45 + max(dot(vec3(0, 1, 0), normalize(fragLightPos - fragPosition)), 0.0); + outColor = vec4(fragColor, 1.0) * texture(texSampler, fragTexCoord) * scale; } diff --git a/Vulkan/res/shaders/shader.vert b/Vulkan/res/shaders/shader.vert index a537cc4..e2f1a3a 100644 --- a/Vulkan/res/shaders/shader.vert +++ b/Vulkan/res/shaders/shader.vert @@ -8,15 +8,19 @@ layout(set = 0, binding = 0) uniform SceneUniformBufferObject vec3 lightPos; } ubo; -layout(location = 0) in vec2 inPosition; +layout(location = 0) in vec3 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; +layout(location = 2) out vec3 fragPosition; +layout(location = 3) out vec3 fragLightPos; void main() { - gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPosition.x, 0.0, inPosition.y, 1.0); + gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPosition, 1.0); fragColor = inColor; fragTexCoord = inTexCoord; + fragPosition = vec3(ubo.model * vec4(inPosition, 1.0)); + fragLightPos = ubo.lightPos; } \ No newline at end of file diff --git a/Vulkan/res/shaders/vert.spv b/Vulkan/res/shaders/vert.spv deleted file mode 100644 index 5fadf46..0000000 Binary files a/Vulkan/res/shaders/vert.spv and /dev/null differ diff --git a/Vulkan/src/CommandBuffer.h b/Vulkan/src/CommandBuffer.h new file mode 100644 index 0000000..c417a4f --- /dev/null +++ b/Vulkan/src/CommandBuffer.h @@ -0,0 +1,94 @@ +#pragma once + +#include "Common.h" +#include "Instance.h" +#include + +enum class CommandBufferType +{ + SingleUse, Dynamic +}; + +class CommandBuffer +{ + CP_DELETE_COPY_AND_MOVE_CTOR(CommandBuffer); +private: + Instance& instance; + + std::vector commandBuffers; + const CommandBufferType type; + VkCommandBuffer currentCommandBuffer{VK_NULL_HANDLE}; + +public: + CommandBuffer(Instance& instance, CommandBufferType type) + : instance{instance}, type{type} + { + if (type == CommandBufferType::Dynamic) + commandBuffers.resize(instance.GetMaxFramesInFlight()); + else + commandBuffers.resize(1); + + VkCommandBufferAllocateInfo allocateInfo{}; + allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocateInfo.commandPool = instance.GetCommandPool(); + allocateInfo.commandBufferCount = commandBuffers.size(); + CP_VK_ASSERT(vkAllocateCommandBuffers(instance.GetDevice(), &allocateInfo, commandBuffers.data()), "Failed to allocate CommandBuffer"); + } + + ~CommandBuffer() + { + vkFreeCommandBuffers(instance.GetDevice(), instance.GetCommandPool(), commandBuffers.size(), commandBuffers.data()); + } + + // TODO: Test as constexpr function to see if it avoids the switch case + void Begin() + { + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + beginInfo.pInheritanceInfo = nullptr; + switch(type) + { + case CommandBufferType::SingleUse: + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + currentCommandBuffer = commandBuffers.front(); + break; + case CommandBufferType::Dynamic: + currentCommandBuffer = commandBuffers[instance.GetFlightIndex()]; + break; + default: + CP_WARN("Unhandled enum case: %d", (int)type); + } + + vkResetCommandBuffer(currentCommandBuffer, 0); + CP_VK_ASSERT(vkBeginCommandBuffer(currentCommandBuffer, &beginInfo), "Failed to begin command buffer"); + } + + void End() + { + vkEndCommandBuffer(currentCommandBuffer); + } + + void Submit() + { + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = ¤tCommandBuffer; + + vkQueueSubmit(instance.GetGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE); + // TODO: if singleUse? + vkQueueWaitIdle(instance.GetGraphicsQueue()); + } + + void SubmitAsGraphicsQueue() + { + instance.SubmitGraphicsQueue({currentCommandBuffer}); + } + + VkCommandBuffer GetHandle() + { + return currentCommandBuffer; + } +}; diff --git a/Vulkan/src/CommandBufferScoped.h b/Vulkan/src/CommandBufferScoped.h new file mode 100644 index 0000000..7461755 --- /dev/null +++ b/Vulkan/src/CommandBufferScoped.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Common.h" +#include "CommandBuffer.h" + +class CommandBufferScoped : public CommandBuffer +{ + CP_DELETE_COPY_AND_MOVE_CTOR(CommandBufferScoped); +public: + CommandBufferScoped(Instance& instance) + : CommandBuffer{instance, CommandBufferType::SingleUse} + { + CommandBuffer::Begin(); + } + + ~CommandBufferScoped() + { + CommandBuffer::End(); + CommandBuffer::Submit(); + } +}; diff --git a/Vulkan/src/Common.h b/Vulkan/src/Common.h index f8ad6f4..2788b96 100644 --- a/Vulkan/src/Common.h +++ b/Vulkan/src/Common.h @@ -1,43 +1,50 @@ #pragma once #include "VulkanException.h" -#include #include -#define CP_DEBUG(format, ...) std::cout << "[DBG] " << StringFormat(format, __VA_ARGS__) << std::endl -#define CP_INFO(format, ...) std::cout << "[INF] " << StringFormat(format, __VA_ARGS__) << std::endl -#define CP_WARN(format, ...) std::cout << "[WRN] " << StringFormat(format, __VA_ARGS__) << std::endl -#define CP_ERR(format, ...) std::cout << "[ERR] " << StringFormat(format, __VA_ARGS__) << std::endl +#define TERM_RED "\x1B[31m" +#define TERM_GREEN "\x1B[32m" +#define TERM_YELLOW "\x1B[33m" +#define TERM_GRAY "\x1B[90m" +#define TERM_CLEAR "\033[0m" + +#define CP_DEBUG(format, ...) std::cout << TERM_GRAY << "[DBG] " << StringFormat(format, __VA_ARGS__) << TERM_CLEAR << std::endl +#define CP_INFO(format, ...) std::cout << "[INF] " << StringFormat(format, __VA_ARGS__) << std::endl +#define CP_WARN(format, ...) std::cout << TERM_YELLOW << "[WRN] " << StringFormat(format, __VA_ARGS__) << TERM_CLEAR << std::endl +#define CP_ERR(format, ...) std::cout << TERM_RED << "[ERR] " << StringFormat(format, __VA_ARGS__) << TERM_CLEAR << std::endl + +// Continue traces, will not print the [XXX] tag before the log +#define CP_DEBUG_CONT(format, ...) std::cout << TERM_GRAY << " " << StringFormat(format, __VA_ARGS__) << TERM_CLEAR << std::endl +#define CP_INFO_CONT(format, ...) std::cout << " " << StringFormat(format, __VA_ARGS__) << std::endl +#define CP_WARN_CONT(format, ...) std::cout << TERM_YELLOW << " " << StringFormat(format, __VA_ARGS__) << TERM_CLEAR << std::endl +#define CP_ERR_CONT(format, ...) std::cout << TERM_RED << " " << StringFormat(format, __VA_ARGS__) << TERM_CLEAR << std::endl #define CP_UNIMPLEMENTED() CP_WARN("%s is unimplemented", __FUNCTION__) -#define CP_ASSERT(Function, format, ...) if(!(Function)) { throw std::runtime_error(StringFormat(format, __VA_ARGS__)); } while(false) -#define CP_VK_ASSERT(Function, format, ...) if(Function != VK_SUCCESS) { throw VulkanException(StringFormat(format, __VA_ARGS__)); } while(false) +#define CP_ASSERT(Function, format, ...) \ + do \ + { \ + if(!(Function)) \ + { \ + CP_ERR(format, __VA_ARGS__); \ + throw std::runtime_error(StringFormat(format, __VA_ARGS__)); \ + } \ + } while(false) +#define CP_VK_ASSERT(Function, format, ...) \ + do \ + { \ + if(Function != VK_SUCCESS) \ + { \ + CP_ERR(format, __VA_ARGS__); \ + throw VulkanException(StringFormat(format, __VA_ARGS__)); \ + } \ + } while(false) #define CP_DELETE_COPY_AND_MOVE_CTOR(ClassName) \ ClassName(ClassName&&) = delete; \ ClassName(const ClassName&) = delete; \ ClassName& operator=(ClassName&&) = delete; \ ClassName& operator=(const ClassName&) = delete -static VkResult vkCreateDebugUtilsMessengerEXT(VkInstance instance, - const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDebugUtilsMessengerEXT* pDebugMessenger) -{ - auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); - if (func != nullptr) - return func(instance, pCreateInfo, pAllocator, pDebugMessenger); - return VK_ERROR_EXTENSION_NOT_PRESENT; -} - -static void vkDestroyDebugUtilsMessengerEXT(VkInstance instance, - VkDebugUtilsMessengerEXT debugMessenger, - const VkAllocationCallbacks* pAllocator) { - auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); - if (func != nullptr) { - func(instance, debugMessenger, pAllocator); - } -} - template std::string StringFormat(const std::string& format, Args... args) { diff --git a/Vulkan/src/DebugMessenger.h b/Vulkan/src/DebugMessenger.h index cb6402c..7cb3a54 100644 --- a/Vulkan/src/DebugMessenger.h +++ b/Vulkan/src/DebugMessenger.h @@ -4,63 +4,85 @@ class DebugMessenger { - CP_DELETE_COPY_AND_MOVE_CTOR(DebugMessenger); + CP_DELETE_COPY_AND_MOVE_CTOR(DebugMessenger); public: - VkInstance instance; - VkDebugUtilsMessengerEXT debugMessenger; + VkInstance instance; + VkDebugUtilsMessengerEXT debugMessenger; - DebugMessenger(VkInstance instance) - : instance{instance} - { + DebugMessenger(VkInstance instance) + : instance{instance} + { #ifndef NDEBUG - VkDebugUtilsMessengerCreateInfoEXT createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; - 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; - 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; - createInfo.pfnUserCallback = DebugCallback; - createInfo.pUserData = nullptr; - CP_VK_ASSERT(vkCreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger), "Failed to initialze debug messenger"); + VkDebugUtilsMessengerCreateInfoEXT createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + 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; + 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; + createInfo.pfnUserCallback = DebugCallback; + createInfo.pUserData = nullptr; + CP_VK_ASSERT(vkCreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger), "Failed to initialze debug messenger"); #endif - } + } - ~DebugMessenger() - { + ~DebugMessenger() + { #ifndef NDEBUG - vkDestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); + vkDestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); #endif - } + } - static void AddRequiredExtensions(std::vector* extensions) - { + static void AddRequiredExtensions(std::vector* extensions) + { #ifndef NDEBUG - extensions->emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + extensions->emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); #endif - } + } - static void AddRequiredLayers(std::vector* layers) - { + static void AddRequiredLayers(std::vector* layers) + { #ifndef NDEBUG - layers->emplace_back("VK_LAYER_KHRONOS_validation"); + layers->emplace_back("VK_LAYER_KHRONOS_validation"); #endif - } + } private: - static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) - { - if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) - { - std::cerr << pCallbackData->pMessage << std::endl; - if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) - throw VulkanException(pCallbackData->pMessage); - } - return VK_FALSE; - } + static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) + { + if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) + { + if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + { + CP_ERR(pCallbackData->pMessage); + throw VulkanException(pCallbackData->pMessage); + } + CP_WARN(pCallbackData->pMessage); + } + return VK_FALSE; + } + static VkResult vkCreateDebugUtilsMessengerEXT(VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pDebugMessenger) + { + auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + if (func != nullptr) + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + static void vkDestroyDebugUtilsMessengerEXT(VkInstance instance, + VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks* pAllocator) { + auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != nullptr) { + func(instance, debugMessenger, pAllocator); + } + } }; diff --git a/Vulkan/src/DescriptorPool.h b/Vulkan/src/DescriptorPool.h index 0d7438f..b35c19e 100644 --- a/Vulkan/src/DescriptorPool.h +++ b/Vulkan/src/DescriptorPool.h @@ -28,6 +28,7 @@ public: createInfo.poolSizeCount = poolSizes.size(); createInfo.pPoolSizes = poolSizes.data(); createInfo.maxSets = DESCRIPTOR_SET_COUNT * instance.GetMaxFramesInFlight(); + createInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; CP_VK_ASSERT(vkCreateDescriptorPool(instance.GetDevice(), &createInfo, nullptr, &descriptorPool), "Failed to initialize descriptor pool"); } @@ -52,4 +53,9 @@ public: return descriptorSets; } + + void FreeDescriptorSets(const std::vector& descriptorSets) + { + vkFreeDescriptorSets(instance.GetDevice(), descriptorPool, descriptorSets.size(), descriptorSets.data()); + } }; diff --git a/Vulkan/src/DescriptorSet.h b/Vulkan/src/DescriptorSet.h index 5c23fe5..b4dd783 100644 --- a/Vulkan/src/DescriptorSet.h +++ b/Vulkan/src/DescriptorSet.h @@ -2,11 +2,11 @@ #include "Common.h" #include "DescriptorPool.h" -#include "Sampler.h" +#include "Texture2D.h" #include "UniformBuffer.h" #include -class DescriptorSet +class DescriptorSet final { CP_DELETE_COPY_AND_MOVE_CTOR(DescriptorSet); private: @@ -23,6 +23,11 @@ public: descriptorSets = descriptorPool.AllocateDescriptorSets(descriptorSetLayout); } + ~DescriptorSet() + { + descriptorPool.FreeDescriptorSets(descriptorSets); + } + void AddUniform(const UniformBuffer& uniformBuffer, uint32_t binding) { for (size_t i = 0; i < instance.GetMaxFramesInFlight(); ++i) { @@ -42,9 +47,9 @@ public: } } - void AddSampler(const Sampler& sampler, uint32_t binding) + void AddTexture2D(const Texture2D& texture2D, uint32_t binding) { - VkDescriptorImageInfo imageInfo = sampler.GetDescriptorImageInfo(); + VkDescriptorImageInfo imageInfo = texture2D.GetDescriptorImageInfo(); for (auto&& descriptorSet : descriptorSets) { VkWriteDescriptorSet descriptorWrite{}; descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; diff --git a/Vulkan/src/FileSystem.h b/Vulkan/src/FileSystem.h index ca2c92f..bb3eff8 100644 --- a/Vulkan/src/FileSystem.h +++ b/Vulkan/src/FileSystem.h @@ -1,12 +1,15 @@ #pragma once +#include +#include + #include "Common.h" #include namespace FileSystem { - static std::vector ReadFile(const std::string& filename) + static std::vector ReadFile(const std::string& filename) { std::ifstream file(filename, std::ios::ate | std::ios::binary); CP_ASSERT(file.is_open(), "Failed to open file"); @@ -19,4 +22,48 @@ namespace FileSystem return buffer; } + + static std::string ReadFileStr(const std::string& filename) + { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + CP_ASSERT(file.is_open(), "Failed to open file"); + + size_t fileSize = (size_t) file.tellg(); + std::string buffer; + buffer.resize(fileSize); + + file.seekg(0); + file.read(buffer.data(), fileSize); + + return buffer; + } + + static void WriteFile(const std::string& filename, const std::string& data) + { + std::ofstream file(filename, std::ios::binary); + CP_ASSERT(file.is_open(), "Failed to open file"); + + file.write(data.c_str(), data.size()); + } + + static void WriteFile(const std::string& filename, const char* data, size_t size) + { + std::ofstream file(filename, std::ios::binary); + CP_ASSERT(file.is_open(), "Failed to open file"); + + file.write(data, size); + } + + static bool FileExists(const std::string& filename) + { + std::ifstream file(filename); + return file.good(); + } + + static int64_t DateModified(const std::string& filename) + { + struct stat result; + CP_ASSERT(stat(filename.c_str(), &result) == 0, "Cannot stat file %s", filename.c_str()); + return (int64_t)result.st_mtime; + } } \ No newline at end of file diff --git a/Vulkan/src/Image.h b/Vulkan/src/Image.h new file mode 100644 index 0000000..024071b --- /dev/null +++ b/Vulkan/src/Image.h @@ -0,0 +1,151 @@ +#pragma once + +#include +#include "Buffer.h" +#include "Common.h" +#include "CommandBufferScoped.h" +#include "Instance.h" + +class Image +{ + Image() = delete; +public: + static void InitializeImage(Instance& instance, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage* image, VkDeviceMemory* imageMemory) + { + VkImageCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + createInfo.imageType = VK_IMAGE_TYPE_2D; + createInfo.extent.width = width; + createInfo.extent.height = height; + createInfo.extent.depth = 1; + createInfo.mipLevels = 1; + createInfo.arrayLayers = 1; + createInfo.format = format; + createInfo.tiling = tiling; + createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + createInfo.usage = usage; + createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.samples = VK_SAMPLE_COUNT_1_BIT; + createInfo.flags = 0; + + CP_VK_ASSERT(vkCreateImage(instance.GetDevice(), &createInfo, nullptr, image), "Failed to initialize image"); + + VkMemoryRequirements memoryRequirements; + vkGetImageMemoryRequirements(instance.GetDevice(), *image, &memoryRequirements); + + VkMemoryAllocateInfo allocateInfo{}; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.allocationSize = memoryRequirements.size; + allocateInfo.memoryTypeIndex = instance.FindMemoryType(memoryRequirements.memoryTypeBits, properties); + + CP_VK_ASSERT(vkAllocateMemory(instance.GetDevice(), &allocateInfo, nullptr, imageMemory), "Failed to initiallizse image memory"); + + vkBindImageMemory(instance.GetDevice(), *image, *imageMemory, 0); + } + + static VkImageView InitializeImageView(Instance& instance, VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) + { + VkImageView imageView; + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = image; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = format; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = aspectFlags; + 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 image view"); + return imageView; + } + + static void TransitionImageLayout(Instance& instance, VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) + { + CommandBufferScoped commandBuffer{instance}; + + 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.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 (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + + if (HasStencilComponent(format)) { + barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + } else { + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + + 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 if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + + srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + } + else + { + throw std::invalid_argument("Unsupported layout transition"); + } + + vkCmdPipelineBarrier(commandBuffer.GetHandle(), srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); + } + + static void CopyBufferToImage(Instance& instance, const Buffer& buffer, VkImage image, uint32_t width, uint32_t height) + { + CommandBufferScoped commandBuffer{instance}; + + 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.GetHandle(), buffer.GetHandle(), image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + } + +private: + static bool HasStencilComponent(VkFormat format) + { + return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT; + } +}; \ No newline at end of file diff --git a/Vulkan/src/Instance.h b/Vulkan/src/Instance.h index 855e58a..177693c 100644 --- a/Vulkan/src/Instance.h +++ b/Vulkan/src/Instance.h @@ -48,10 +48,10 @@ public: InitializeSurface(); SelectPhysicalDevice(); InitializeLogicalDevice(); - InitializeSwapChain(); InitializeCommandPool(); + InitializeSwapChain(); InitializeSyncObjects(); - std::cout << "Initialized Vulkan in " << timer.Elapsed() << " seconds" << std::endl; + CP_INFO("Initialized Vulkan in %f seconds", timer.Elapsed()); } ~Instance() @@ -211,10 +211,10 @@ private: std::vector extensions{extensionCount}; vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); - std::cout << "Supported Extensions: " << std::endl; + CP_INFO("Supported Extensions:"); for (auto&& extension : extensions) { - std::cout << "\t" << extension.extensionName << std::endl; + CP_INFO_CONT("\t%s", extension.extensionName); } std::vector layers{}; @@ -249,12 +249,12 @@ private: std::vector devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); - std::cout << "Available devices: " << std::endl; + CP_INFO("Available devices:"); for (auto&& device : devices) { VkPhysicalDeviceProperties deviceProperties; vkGetPhysicalDeviceProperties(device, &deviceProperties); - std::cout << "\t" << deviceProperties.deviceName << std::endl; + CP_INFO_CONT("\t%s", deviceProperties.deviceName); } for (auto&& device : devices) { @@ -263,7 +263,7 @@ private: VkPhysicalDeviceProperties deviceProperties; vkGetPhysicalDeviceProperties(device, &deviceProperties); physicalDevice = device; - std::cout << "Selecting device: " << deviceProperties.deviceName << std::endl; + CP_INFO("Selecting device: %s", deviceProperties.deviceName); break; } } @@ -362,10 +362,10 @@ private: std::vector availableLayers(layerCount); vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - std::cout << "Supported Layers: " << std::endl; + CP_INFO("Supported Layers:"); for (auto&& availableLayer : availableLayers) { - std::cout << "\t" << availableLayer.layerName << std::endl; + CP_INFO_CONT("\t%s", availableLayer.layerName); } for (auto&& layer : layers) diff --git a/Vulkan/src/Pipeline.h b/Vulkan/src/Pipeline.h index f6d9238..ac66894 100644 --- a/Vulkan/src/Pipeline.h +++ b/Vulkan/src/Pipeline.h @@ -3,7 +3,9 @@ #include "Common.h" #include "Instance.h" #include "FileSystem.h" +#include "DescriptorSet.h" #include "PipelineCreator.h" +#include "Shader.h" #include #include @@ -16,6 +18,7 @@ private: Instance& instance; std::vector descriptorSetLayouts{}; + std::vector boundDescriptorSets; VkPipelineLayout pipelineLayout; VkPipeline graphicsPipeline; @@ -55,9 +58,15 @@ public: vkCmdSetScissor(commandBuffer, 0, 1, &scissor); } - VkPipelineLayout GetPipelineLayout() const + void SetDescriptorSet(uint32_t setIndex, const DescriptorSet& descriptorSet) { - return pipelineLayout; + CP_ASSERT(setIndex < boundDescriptorSets.size(), "DescriptorSet index is out of bounds"); + boundDescriptorSets[setIndex] = descriptorSet.GetHandle(); + } + + void BindDescriptorSets(VkCommandBuffer commandBuffer) + { + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, boundDescriptorSets.size(), boundDescriptorSets.data(), 0, nullptr); } VkDescriptorSetLayout GetDescriptorSetLayout(uint32_t setIndex) const @@ -68,21 +77,27 @@ public: private: void InitializeDescriptorSetLayout(const PipelineCreator& creator) { + boundDescriptorSets.resize(creator.descriptorSetLayouts.size()); descriptorSetLayouts.resize(creator.descriptorSetLayouts.size()); int i = 0; - for (auto&& binding : creator.descriptorSetLayouts) + for (auto&& bindings : creator.descriptorSetLayouts) { - VkDescriptorSetLayoutBinding layoutBinding{}; - layoutBinding.binding = 0; - layoutBinding.descriptorType = binding.second.type; - layoutBinding.descriptorCount = 1; - layoutBinding.stageFlags = binding.second.flags; - layoutBinding.pImmutableSamplers = nullptr; + std::vector layoutBindings{bindings.second.size()}; + int j = 0; + for (auto&& binding : bindings.second) + { + layoutBindings[j].binding = binding.binding; + layoutBindings[j].descriptorType = binding.type; + layoutBindings[j].descriptorCount = binding.count; + layoutBindings[j].stageFlags = binding.flags; + layoutBindings[j].pImmutableSamplers = nullptr; + j++; + } VkDescriptorSetLayoutCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - createInfo.bindingCount = 1; - createInfo.pBindings = &layoutBinding; + createInfo.bindingCount = layoutBindings.size(); + createInfo.pBindings = layoutBindings.data(); CP_VK_ASSERT(vkCreateDescriptorSetLayout(instance.GetDevice(), &createInfo, nullptr, &descriptorSetLayouts[i++]), "Failed to initialize descriptor set layout"); } @@ -90,24 +105,7 @@ private: void InitializePipeline(const PipelineCreator& creator) { - std::vector vertShaderCode = FileSystem::ReadFile(creator.vertexShader); - std::vector fragShaderCode = FileSystem::ReadFile(creator.fragmentShader); - - VkShaderModule vertShaderModule = InitializeShaderModule(vertShaderCode); - VkShaderModule fragShaderModule = InitializeShaderModule(fragShaderCode); - - VkPipelineShaderStageCreateInfo shaderStages[2]; - shaderStages[0] = {}; - shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - shaderStages[0].module = vertShaderModule; - shaderStages[0].pName = "main"; - - shaderStages[1] = {}; - shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - shaderStages[1].module = fragShaderModule; - shaderStages[1].pName = "main"; + Shader shader{instance, ShaderType::GlslFile, creator.vertexShader, creator.fragmentShader}; VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo{}; vertexInputCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; @@ -173,6 +171,19 @@ private: multisampleCreateInfo.alphaToCoverageEnable = VK_FALSE; multisampleCreateInfo.alphaToOneEnable = VK_FALSE; + VkPipelineDepthStencilStateCreateInfo depthStencilCreateInfo{}; + depthStencilCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencilCreateInfo.depthTestEnable = VK_TRUE; + depthStencilCreateInfo.depthWriteEnable = VK_TRUE; + depthStencilCreateInfo.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencilCreateInfo.depthBoundsTestEnable = VK_FALSE; + depthStencilCreateInfo.minDepthBounds = 0.0f; + depthStencilCreateInfo.maxDepthBounds = 1.0f; + depthStencilCreateInfo.stencilTestEnable = VK_FALSE; + depthStencilCreateInfo.front = {}; + depthStencilCreateInfo.back = {}; + + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; // TODO: Add to PipelineCreator colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | @@ -206,16 +217,17 @@ private: CP_VK_ASSERT(vkCreatePipelineLayout(instance.GetDevice(), &pipelineLayoutCreateInfo, nullptr, &pipelineLayout), "Failed to initialize pipeline layout"); + const std::vector& shaderStages = shader.GetShaderStages(); VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo{}; graphicsPipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - graphicsPipelineCreateInfo.stageCount = 2; - graphicsPipelineCreateInfo.pStages = shaderStages; + graphicsPipelineCreateInfo.stageCount = shaderStages.size(); + graphicsPipelineCreateInfo.pStages = shaderStages.data(); graphicsPipelineCreateInfo.pVertexInputState = &vertexInputCreateInfo; graphicsPipelineCreateInfo.pInputAssemblyState = &inputAssemblyCreateInfo; graphicsPipelineCreateInfo.pViewportState = &viewportStateCreateInfo; graphicsPipelineCreateInfo.pRasterizationState = &rasterizerCreateInfo; graphicsPipelineCreateInfo.pMultisampleState = &multisampleCreateInfo; - graphicsPipelineCreateInfo.pDepthStencilState = nullptr; + graphicsPipelineCreateInfo.pDepthStencilState = &depthStencilCreateInfo; graphicsPipelineCreateInfo.pColorBlendState = &colorBlendCreateInfo; graphicsPipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo; graphicsPipelineCreateInfo.layout = pipelineLayout; @@ -225,9 +237,6 @@ private: graphicsPipelineCreateInfo.basePipelineIndex = -1; CP_VK_ASSERT(vkCreateGraphicsPipelines(instance.GetDevice(), VK_NULL_HANDLE, 1, &graphicsPipelineCreateInfo, nullptr, &graphicsPipeline), "Failed to initialize graphics pipeline"); - - vkDestroyShaderModule(instance.GetDevice(), vertShaderModule, nullptr); - vkDestroyShaderModule(instance.GetDevice(), fragShaderModule, nullptr); } VkShaderModule InitializeShaderModule(const std::vector& code) diff --git a/Vulkan/src/PipelineCreator.h b/Vulkan/src/PipelineCreator.h index 63553c0..a57d343 100644 --- a/Vulkan/src/PipelineCreator.h +++ b/Vulkan/src/PipelineCreator.h @@ -1,19 +1,23 @@ #pragma once #include "Common.h" +#include "VertexDescriptor.h" + #include -#include +#include class PipelineCreator { - struct DescriptorSetLayout + struct DescriptorSetBinding { + uint32_t binding; VkDescriptorType type; + uint32_t count; VkShaderStageFlags flags; }; friend class Pipeline; private: - std::map descriptorSetLayouts{}; + std::map> descriptorSetLayouts{}; std::string vertexShader; std::string fragmentShader; @@ -22,7 +26,6 @@ private: VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT; VkFrontFace frontFace = VK_FRONT_FACE_CLOCKWISE; - public: PipelineCreator(const std::string& vertexShader, const std::string& fragmentShader) : vertexShader{vertexShader}, fragmentShader{fragmentShader} @@ -33,9 +36,10 @@ public: vertexDescriptor = descriptor; } - void AddDescriptorSetLayoutBinding(uint32_t set, VkDescriptorType type, VkShaderStageFlags stageFlags) + void AddDescriptorSetLayoutBinding(uint32_t set, uint32_t binding, VkDescriptorType type, uint32_t count, VkShaderStageFlags stageFlags) { - descriptorSetLayouts.emplace(set, DescriptorSetLayout{type, stageFlags}); + CP_ASSERT(set <= descriptorSetLayouts.size(), "Cannot add descriptor set with set number greater than the current set count"); + descriptorSetLayouts[set].emplace_back(DescriptorSetBinding{binding, type, count, stageFlags}); } void SetPrimitiveTopology(VkPrimitiveTopology primitiveTopology) diff --git a/Vulkan/src/Sampler.h b/Vulkan/src/Sampler.h deleted file mode 100644 index 58849ff..0000000 --- a/Vulkan/src/Sampler.h +++ /dev/null @@ -1,61 +0,0 @@ -#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/Shader.h b/Vulkan/src/Shader.h new file mode 100644 index 0000000..c5a4726 --- /dev/null +++ b/Vulkan/src/Shader.h @@ -0,0 +1,151 @@ +#pragma once + +#include + +#include "FileSystem.h" +#include "Common.h" +#include "Instance.h" + +enum class ShaderType +{ + GlslFile, GlslCode, SpvFile, SpvCode +}; + +class Shader +{ + CP_DELETE_COPY_AND_MOVE_CTOR(Shader); +private: + Instance& instance; + + VkShaderModule vertShaderModule; + VkShaderModule fragShaderModule; + std::vector shaderStages; +public: + Shader(Instance& instance, ShaderType shaderType, const std::string& vertexInput, const std::string& fragmentInput) + : instance{instance} + { + switch (shaderType) + { + case ShaderType::GlslCode: + vertShaderModule = InitializeShaderModuleFromGlslCode(vertexInput, shaderc_vertex_shader); + fragShaderModule = InitializeShaderModuleFromGlslCode(fragmentInput, shaderc_fragment_shader); + break; + case ShaderType::GlslFile: + vertShaderModule = InitializeShaderModuleFromGlslFile(vertexInput, shaderc_vertex_shader); + fragShaderModule = InitializeShaderModuleFromGlslFile(fragmentInput, shaderc_fragment_shader); + break; + case ShaderType::SpvCode: + vertShaderModule = InitializeShaderModule(vertexInput); + fragShaderModule = InitializeShaderModule(fragmentInput); + break; + case ShaderType::SpvFile: + vertShaderModule = InitializeShaderModule(FileSystem::ReadFile(vertexInput)); + fragShaderModule = InitializeShaderModule(FileSystem::ReadFile(fragmentInput)); + break; + default: + CP_ASSERT(false, "Unreachable switch case %d", (int)shaderType); + } + + shaderStages.resize(2); + shaderStages[0] = {}; + shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + shaderStages[0].module = vertShaderModule; + shaderStages[0].pName = "main"; + + shaderStages[1] = {}; + shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shaderStages[1].module = fragShaderModule; + shaderStages[1].pName = "main"; + } + + ~Shader() + { + vkDestroyShaderModule(instance.GetDevice(), vertShaderModule, nullptr); + vkDestroyShaderModule(instance.GetDevice(), fragShaderModule, nullptr); + } + + const std::vector GetShaderStages() const + { + return shaderStages; + } + +private: + VkShaderModule InitializeShaderModule(const std::vector& codeSpv) + { + return InitializeShaderModule(codeSpv.data(), codeSpv.size() * sizeof(uint32_t)); + } + + VkShaderModule InitializeShaderModule(const std::string& codeSpv) + { + return InitializeShaderModule(reinterpret_cast(codeSpv.data()), codeSpv.size()); + } + + VkShaderModule InitializeShaderModule(const std::vector& codeSpv) + { + return InitializeShaderModule(reinterpret_cast(codeSpv.data()), codeSpv.size()); + } + + VkShaderModule InitializeShaderModuleFromGlslFile(const std::string& filename, shaderc_shader_kind shaderType) + { + std::string spvFilename = filename + ".spv"; + try + { + if (FileSystem::FileExists(spvFilename)) + { + if (FileSystem::DateModified(filename) < FileSystem::DateModified(spvFilename)) + { + CP_DEBUG("Loading cached shader file: %s", filename.c_str()); + std::vector data = FileSystem::ReadFile(spvFilename); + CP_ASSERT(data.size() % 4 == 0, "Spv data size is not a factor of 4"); + return InitializeShaderModule((const uint32_t*)data.data(), data.size()); + } + } + } + catch (const std::runtime_error& e) + { + CP_WARN("Cached shader file is invalid, recreating it"); + } + CP_DEBUG("Compiling shader file: %s", filename.c_str()); + shaderc::Compiler compiler; + shaderc::CompileOptions options; + + options.SetOptimizationLevel(shaderc_optimization_level_size); + + std::vector glslCode = FileSystem::ReadFile(filename); + shaderc::SpvCompilationResult result = compiler.CompileGlslToSpv(glslCode.data(), glslCode.size(), shaderType, filename.c_str(), options); + CP_ASSERT(result.GetCompilationStatus() == shaderc_compilation_status_success, "Failed to compile shader: %s\n%s", filename.c_str(), result.GetErrorMessage().c_str()); + + std::vector data{result.cbegin(), result.cend()}; + FileSystem::WriteFile(spvFilename, (const char*)data.data(), data.size() * sizeof(uint32_t)); + return InitializeShaderModule(data.data(), data.size() * sizeof(uint32_t)); + } + + VkShaderModule InitializeShaderModuleFromGlslCode(const std::string& code, shaderc_shader_kind shaderType) + { + shaderc::Compiler compiler; + shaderc::CompileOptions options; + + options.SetOptimizationLevel(shaderc_optimization_level_size); + + shaderc::SpvCompilationResult result = compiler.CompileGlslToSpv(code.data(), shaderType, "inline_shader_code", options); + CP_ASSERT(result.GetCompilationStatus() == shaderc_compilation_status_success, "Failed to compile inline shader code: %s", result.GetErrorMessage()); + + std::vector data{result.cbegin(), result.cend()}; + return InitializeShaderModule(data.data(), data.size() * sizeof(uint32_t)); + } + + VkShaderModule InitializeShaderModule(const uint32_t* data, size_t size) + { + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = size; + createInfo.pCode = data; + + VkShaderModule shaderModule; + CP_VK_ASSERT(vkCreateShaderModule(instance.GetDevice(), &createInfo, nullptr, &shaderModule), "Failed to initialize shader module"); + + return shaderModule; + } +}; diff --git a/Vulkan/src/SwapChain.cpp b/Vulkan/src/SwapChain.cpp index 5f716ee..c54699f 100644 --- a/Vulkan/src/SwapChain.cpp +++ b/Vulkan/src/SwapChain.cpp @@ -1,5 +1,9 @@ -#include "QueueFamilies.h" +#include "SwapChain.h" + +#include "Image.h" #include "Instance.h" +#include "QueueFamilies.h" + #include #include #include @@ -35,6 +39,7 @@ SwapChain::SwapChain(Instance& instance) { Initialize(); InitializeImageViews(); + InitializeDepthBuffer(); InitializeRenderPass(); InitializeFramebuffers(); } @@ -111,6 +116,7 @@ void SwapChain::Recreate() Initialize(); InitializeImageViews(); + InitializeDepthBuffer(); InitializeFramebuffers(); } @@ -170,24 +176,19 @@ void SwapChain::InitializeImageViews() imageViews.resize(images.size()); for (size_t i = 0; i < images.size(); i++) { - VkImageViewCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.image = images[i]; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = imageFormat; - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - 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, &imageViews[i]), "Failed to initialize swapchain image view"); + imageViews[i] = Image::InitializeImageView(instance, images[i], imageFormat, VK_IMAGE_ASPECT_COLOR_BIT); } } +void SwapChain::InitializeDepthBuffer() +{ + VkFormat depthFormat = SelectDepthFormat(); + Image::InitializeImage(instance, extent.width, extent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &depthImage, &depthImageMemory); + Image::TransitionImageLayout(instance, depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + depthImageView = Image::InitializeImageView(instance, depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT); +} + void SwapChain::InitializeRenderPass() { VkAttachmentDescription colorAttachment{}; @@ -200,28 +201,43 @@ void SwapChain::InitializeRenderPass() colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentDescription depthAttachment{}; + depthAttachment.format = SelectDepthFormat(); + depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + VkAttachmentReference colorAttachmentRef{}; colorAttachmentRef.attachment = 0; colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkAttachmentReference depthAttachmentRef{}; + depthAttachmentRef.attachment = 1; + depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentRef; + subpass.pDepthStencilAttachment = &depthAttachmentRef; VkSubpassDependency dependency{}; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + std::vector attachments{colorAttachment, depthAttachment}; VkRenderPassCreateInfo renderPassCreateInfo{}; renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCreateInfo.attachmentCount = 1; - renderPassCreateInfo.pAttachments = &colorAttachment; + renderPassCreateInfo.attachmentCount = attachments.size(); + renderPassCreateInfo.pAttachments = attachments.data(); renderPassCreateInfo.subpassCount = 1; renderPassCreateInfo.pSubpasses = &subpass; renderPassCreateInfo.dependencyCount = 1; @@ -236,13 +252,13 @@ void SwapChain::InitializeFramebuffers() for (size_t i = 0; i < imageViews.size(); ++i) { - VkImageView attachments[] = {imageViews[i]}; + std::vector attachments{imageViews[i], depthImageView}; VkFramebufferCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; createInfo.renderPass = renderPass; - createInfo.attachmentCount = 1; - createInfo.pAttachments = attachments; + createInfo.attachmentCount = attachments.size(); + createInfo.pAttachments = attachments.data(); createInfo.width = extent.width; createInfo.height = extent.height; createInfo.layers = 1; @@ -253,6 +269,9 @@ void SwapChain::InitializeFramebuffers() void SwapChain::Destroy() { + vkDestroyImage(instance.GetDevice(), depthImage, nullptr); + vkFreeMemory(instance.GetDevice(), depthImageMemory, nullptr); + vkDestroyImageView(instance.GetDevice(), depthImageView, nullptr); for (auto&& framebuffer : framebuffers) { vkDestroyFramebuffer(instance.GetDevice(), framebuffer, nullptr); @@ -276,6 +295,29 @@ VkSurfaceFormatKHR SwapChain::SelectSwapSurfaceFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) +{ + for (VkFormat format : candidates) + { + VkFormatProperties properties; + vkGetPhysicalDeviceFormatProperties(instance.GetPhysicalDevice(), format, &properties); + if(tiling == VK_IMAGE_TILING_LINEAR && (properties.linearTilingFeatures & features) == features) + { + return format; + } + else if (tiling == VK_IMAGE_TILING_OPTIMAL && (properties.optimalTilingFeatures & features) == features) + { + return format; + } + } + throw std::runtime_error("Failed to select supported format"); +} + VkPresentModeKHR SwapChain::SelectSwapPresentMode(const std::vector& availablePresentModes) { return VK_PRESENT_MODE_FIFO_KHR; diff --git a/Vulkan/src/SwapChain.h b/Vulkan/src/SwapChain.h index 80a82b5..a150ef5 100644 --- a/Vulkan/src/SwapChain.h +++ b/Vulkan/src/SwapChain.h @@ -1,7 +1,10 @@ #pragma once +#include "Common.h" + #include #include +#include class Instance; @@ -26,6 +29,9 @@ private: VkRenderPass renderPass; VkFormat imageFormat; VkExtent2D extent; + VkImage depthImage; + VkImageView depthImageView; + VkDeviceMemory depthImageMemory; std::vector imageViews; std::vector images; std::vector framebuffers; @@ -48,11 +54,14 @@ public: private: void Initialize(); void InitializeImageViews(); + void InitializeDepthBuffer(); void InitializeRenderPass(); void InitializeFramebuffers(); void Destroy(); VkSurfaceFormatKHR SelectSwapSurfaceFormat(const std::vector& availableFormats); + VkFormat SelectDepthFormat(); + VkFormat SelectSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features); VkPresentModeKHR SelectSwapPresentMode(const std::vector& availablePresentModes); VkExtent2D SelectSwapExtent(GLFWwindow* window, const VkSurfaceCapabilitiesKHR& capabilities); }; diff --git a/Vulkan/src/Texture2D.h b/Vulkan/src/Texture2D.h new file mode 100644 index 0000000..caa8b66 --- /dev/null +++ b/Vulkan/src/Texture2D.h @@ -0,0 +1,115 @@ +#pragma once + +#include +#include "CommandBufferScoped.h" +#include "Common.h" +#include "Image.h" +#include "Instance.h" + +#define STB_IMAGE_IMPLEMENTATION +#include + +class Texture2D +{ + CP_DELETE_COPY_AND_MOVE_CTOR(Texture2D); +private: + Instance& instance; + + VkImage image; + VkDeviceMemory imageMemory; + VkImageView imageView; + VkSampler sampler; +public: + Texture2D(Instance& instance, const std::string& filename) + : instance{instance} + { + InitializeTextureImage(filename); + InitializeSampler(); + } + + ~Texture2D() + { + vkDestroyImage(instance.GetDevice(), image, nullptr); + vkFreeMemory(instance.GetDevice(), imageMemory, nullptr); + vkDestroyImageView(instance.GetDevice(), imageView, nullptr); + vkDestroySampler(instance.GetDevice(), sampler, nullptr); + } + + VkDescriptorImageInfo GetDescriptorImageInfo() const + { + VkDescriptorImageInfo imageInfo{}; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfo.imageView = imageView; + imageInfo.sampler = sampler; + + return imageInfo; + } +private: + void InitializeTextureImage(const std::string& filename) + { + int texWidth; + int texHeight; + int texChannels; + stbi_uc* pixels = stbi_load(filename.c_str(), &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); + + Image::InitializeImage(instance, texWidth, texHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &image, &imageMemory); + Image::TransitionImageLayout(instance, image, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + Image::CopyBufferToImage(instance, stagingBuffer, image, texWidth, texHeight); + Image::TransitionImageLayout(instance, image, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + imageView = Image::InitializeImageView(instance, image, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT); + } + + void InitializeSampler() + { + 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_CLAMP_TO_EDGE; + createInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + createInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + 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, &sampler), "Failed to initialize texture sampler"); + } + + VkImageView CreateImageView(VkImage image) + { + VkImageView imageView; + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = image; + 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; + } + +}; + diff --git a/Vulkan/src/UniformBuffer.h b/Vulkan/src/UniformBuffer.h index 3853b06..544b62d 100644 --- a/Vulkan/src/UniformBuffer.h +++ b/Vulkan/src/UniformBuffer.h @@ -2,7 +2,6 @@ #include "Common.h" #include "Buffer.h" -#include "Pipeline.h" #include class UniformBuffer : public Buffer @@ -21,11 +20,6 @@ public: 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); - } - VkDescriptorBufferInfo GetDescriptorBufferInfo(int index) const { VkDescriptorBufferInfo bufferInfo{}; diff --git a/Vulkan/src/Vertex.h b/Vulkan/src/Vertex.h index 9700f2f..ae15341 100644 --- a/Vulkan/src/Vertex.h +++ b/Vulkan/src/Vertex.h @@ -5,14 +5,14 @@ #include "VertexDescriptor.h" struct Vertex { - glm::vec2 pos; + glm::vec3 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, 0, VK_FORMAT_R32G32B32_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; diff --git a/Vulkan/src/main.cpp b/Vulkan/src/main.cpp index 0fd310c..0f03519 100644 --- a/Vulkan/src/main.cpp +++ b/Vulkan/src/main.cpp @@ -1,36 +1,38 @@ -#include "FileSystem.h" #include "Buffer.h" -#include "IndexBuffer.h" -#include "VertexBuffer.h" -#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 "IndexBuffer.h" +#include "Instance.h" +#include "Pipeline.h" +#include "Texture2D.h" +#include "Timer.h" +#include "UniformBuffer.h" +#include "Vertex.h" +#include "VertexBuffer.h" +#include +#include +#include +#include #include -#include #include #include -#include -#define STB_IMAGE_IMPLEMENTATION -#include -#include -#include +#include const std::vector vertices = { - 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}} + Vertex{{-0.5f, 0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + Vertex{{ 0.5f, 0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, + Vertex{{ 0.5f, 0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, + Vertex{{-0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, + Vertex{{-0.5f, 0.0f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + Vertex{{ 0.5f, 0.0f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, + Vertex{{ 0.5f, 0.0f, 0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, + Vertex{{-0.5f, 0.0f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, }; const std::vector indices = { - 0, 1, 2, 2, 3, 0 + 0, 1, 2, 2, 3, 0, + 4, 5, 6, 6, 7, 4 }; struct alignas(64) ShaderUniform @@ -46,40 +48,30 @@ class Application final private: std::unique_ptr instance; std::unique_ptr graphicsPipeline; + std::unique_ptr texture2D; + std::unique_ptr shaderUniformBuffer; + std::unique_ptr descriptorPool; + std::unique_ptr descriptorSet; std::unique_ptr vertexBuffer; std::unique_ptr indexBuffer; - 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; + std::unique_ptr commandBuffer; public: Application() { InitializeInstance(); InitializeGraphicsPipeline(); - InitializeTextureImage(); - InitializeTextureImageView(); InitializeTextureSampler(); InitializeUniformBuffer(); InitializeDescriptorSets(); InitializeVertexBuffer(); InitializeIndexBuffer(); - InitializeCommandBuffers(); + InitializeCommandBuffer(); } ~Application() { vkDeviceWaitIdle(instance->GetDevice()); - vkDestroyImage(instance->GetDevice(), textureImage, nullptr); - vkFreeMemory(instance->GetDevice(), textureImageMemory, nullptr); - vkDestroyImageView(instance->GetDevice(), textureImageView, nullptr); } Application(Application&&) = delete; @@ -92,9 +84,9 @@ public: if (!instance->BeginPresent()) return true; - RecordCommandBuffer(commandBuffers[instance->GetFlightIndex()]); + RecordCommandBuffer(); + commandBuffer->SubmitAsGraphicsQueue(); - instance->SubmitGraphicsQueue(std::vector{commandBuffers[instance->GetFlightIndex()]}); return instance->EndPresent(); } @@ -105,184 +97,9 @@ 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); - + texture2D = std::make_unique(*instance, "res/textures/texture.png"); } void InitializeUniformBuffer() @@ -294,18 +111,16 @@ private: { 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); + descriptorSet = std::make_unique(*instance, *descriptorPool, graphicsPipeline->GetDescriptorSetLayout(0)); + descriptorSet->AddUniform(*shaderUniformBuffer, 0); + descriptorSet->AddTexture2D(*texture2D, 1); } void InitializeGraphicsPipeline() { - PipelineCreator creator{"res/shaders/vert.spv", "res/shaders/frag.spv"}; - 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); + PipelineCreator creator{"res/shaders/shader.vert", "res/shaders/shader.frag"}; + creator.AddDescriptorSetLayoutBinding(0, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT); + creator.AddDescriptorSetLayoutBinding(0, 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT); creator.SetVertexDescriptor(Vertex::GetDescriptor()); creator.SetCullMode(VK_CULL_MODE_NONE); graphicsPipeline = std::make_unique(*instance, creator); @@ -324,30 +139,17 @@ private: indexBuffer->UpdateStaging((void*)indices.data()); } - void InitializeCommandBuffers() + void InitializeCommandBuffer() { - commandBuffers.resize(instance->GetMaxFramesInFlight()); - VkCommandBufferAllocateInfo allocateInfo{}; - allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocateInfo.commandPool = instance->GetCommandPool(); - allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocateInfo.commandBufferCount = commandBuffers.size(); - - CP_VK_ASSERT(vkAllocateCommandBuffers(instance->GetDevice(), &allocateInfo, commandBuffers.data()), "Failed to initialize command buffer"); + commandBuffer = std::make_unique(*instance, CommandBufferType::Dynamic); } - void RecordCommandBuffer(VkCommandBuffer commandBuffer) + void RecordCommandBuffer() { - vkResetCommandBuffer(commandBuffer, 0); - - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = 0; - beginInfo.pInheritanceInfo = nullptr; - - CP_VK_ASSERT(vkBeginCommandBuffer(commandBuffer, &beginInfo), "Failed to begin command buffer"); - - VkClearValue clearValue = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + commandBuffer->Begin(); + std::vector clearValues{2}; + clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}}; + clearValues[1].depthStencil = {1.0f, 0}; // TODO: framebuffer->Bind(); VkRenderPassBeginInfo renderPassBeginInfo{}; @@ -356,39 +158,37 @@ private: renderPassBeginInfo.framebuffer = instance->GetSwapChain().GetFramebuffer(); renderPassBeginInfo.renderArea.offset = {0, 0}; renderPassBeginInfo.renderArea.extent = instance->GetSwapChain().GetExtent(); - renderPassBeginInfo.clearValueCount = 1; - renderPassBeginInfo.pClearValues = &clearValue; - vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + renderPassBeginInfo.clearValueCount = clearValues.size(); + renderPassBeginInfo.pClearValues = clearValues.data(); + vkCmdBeginRenderPass(commandBuffer->GetHandle(), &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - graphicsPipeline->Bind(commandBuffer); + graphicsPipeline->Bind(commandBuffer->GetHandle()); UpdateUniformBuffer(); - vertexBuffer->Bind(commandBuffer); - indexBuffer->Bind(commandBuffer); - shaderUniformBuffer->Bind(commandBuffer); + vertexBuffer->Bind(commandBuffer->GetHandle()); + indexBuffer->Bind(commandBuffer->GetHandle()); - 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); + graphicsPipeline->SetDescriptorSet(0, *descriptorSet); + graphicsPipeline->BindDescriptorSets(commandBuffer->GetHandle()); - indexBuffer->Draw(commandBuffer); + indexBuffer->Draw(commandBuffer->GetHandle()); - vkCmdEndRenderPass(commandBuffer); - CP_VK_ASSERT(vkEndCommandBuffer(commandBuffer), "Failed to end command buffer"); + vkCmdEndRenderPass(commandBuffer->GetHandle()); + commandBuffer->End(); } void UpdateUniformBuffer() { - static Timer startTimer; + static Timer startTimer; float time = startTimer.Elapsed(); ShaderUniform shaderUniform; - shaderUniform.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); - shaderUniform.projection = glm::perspective(glm::radians(45.0f), instance->GetSwapChain().GetExtent().width / (float) instance->GetSwapChain().GetExtent().height, 0.1f, 10.0f); + shaderUniform.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + shaderUniform.projection = glm::perspective(glm::radians(45.0f), instance->GetSwapChain().GetExtent().width / (float)instance->GetSwapChain().GetExtent().height, 0.1f, 10.0f); shaderUniform.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f)); shaderUniform.projection[1][1] *= -1; + shaderUniform.lightPos = glm::rotate(glm::mat4{1.0f}, time * glm::radians(45.0f), glm::vec3(0, 1, 0)) * glm::vec4{0.3, 0.1, 0, 1}; shaderUniformBuffer->Update(shaderUniform); } @@ -419,7 +219,7 @@ int main() glfwPollEvents(); if (timer.Elapsed() >= 1.0) { - std::cout << frames << "fps" << std::endl; + CP_DEBUG("%d fps", frames); frames = 0; timer.Start(); }