diff --git a/src/editor/shaders/triangle.frag b/src/editor/shaders/triangle.frag index 0ea6951..223775f 100644 --- a/src/editor/shaders/triangle.frag +++ b/src/editor/shaders/triangle.frag @@ -5,7 +5,7 @@ layout(location = 1) in vec2 uv; layout(location = 0) out vec4 color; -layout(binding = 0) uniform sampler2D albedo; +layout(binding = 1) uniform sampler2D albedo; void main() { color = texture(albedo, uv) * vec4(vertex_color, 1.0); diff --git a/src/engine/Buffer.h b/src/engine/Buffer.h index 382e543..8838d25 100644 --- a/src/engine/Buffer.h +++ b/src/engine/Buffer.h @@ -18,10 +18,6 @@ namespace Vixen { virtual ~Buffer() = default; - virtual char* map() = 0; - - virtual void unmap() = 0; - /** * Map the buffer, write to it and unmap the buffer from host memory. * @param data A pointer pointing to the start of the data. @@ -34,6 +30,11 @@ namespace Vixen { [[nodiscard]] Usage getBufferUsage() const; + private: + virtual char* map() = 0; + + virtual void unmap() = 0; + protected: const Usage bufferUsage; diff --git a/src/engine/vk/CMakeLists.txt b/src/engine/vk/CMakeLists.txt index 182bbc8..db2b4d6 100644 --- a/src/engine/vk/CMakeLists.txt +++ b/src/engine/vk/CMakeLists.txt @@ -59,6 +59,8 @@ add_library( VkDescriptorSetLayout.h VkDescriptorSet.cpp VkDescriptorSet.h + VkSampler.cpp + VkSampler.h ) target_link_libraries( VkVixen @@ -70,6 +72,7 @@ target_link_libraries( spirv-cross-core spirv-cross-glsl spirv-cross-reflect + freeimage ) target_compile_definitions(VkVixen PRIVATE -DVK_NO_PROTOTYPES) diff --git a/src/engine/vk/Device.cpp b/src/engine/vk/Device.cpp index ab0aaf4..b7d31e1 100644 --- a/src/engine/vk/Device.cpp +++ b/src/engine/vk/Device.cpp @@ -8,15 +8,15 @@ namespace Vixen::Vk { const std::vector &extensions, GraphicsCard gpu, VkSurfaceKHR surface - ) : device(VK_NULL_HANDLE), + ) : gpu(gpu), + device(VK_NULL_HANDLE), allocator(VK_NULL_HANDLE), - gpu(gpu), surface(surface) { graphicsQueueFamily = gpu.getQueueFamilyWithFlags(VK_QUEUE_GRAPHICS_BIT)[0]; presentQueueFamily = gpu.getSurfaceSupportedQueues(surface)[0]; transferQueueFamily = gpu.getQueueFamilyWithFlags(VK_QUEUE_TRANSFER_BIT)[0]; - std::set queueFamilies = { + std::set queueFamilies = { graphicsQueueFamily.index, presentQueueFamily.index, transferQueueFamily.index, diff --git a/src/engine/vk/VkBuffer.cpp b/src/engine/vk/VkBuffer.cpp index c35c5e3..ea3beef 100644 --- a/src/engine/vk/VkBuffer.cpp +++ b/src/engine/vk/VkBuffer.cpp @@ -5,7 +5,8 @@ namespace Vixen::Vk { : Buffer(bufferUsage, size), device(device), allocation(VK_NULL_HANDLE), - buffer(VK_NULL_HANDLE) { + buffer(VK_NULL_HANDLE), + data(nullptr) { VmaAllocationCreateFlags allocationFlags = 0; VkBufferUsageFlags bufferUsageFlags = 0; VkMemoryPropertyFlags requiredFlags = 0; @@ -65,38 +66,29 @@ namespace Vixen::Vk { ), "Failed to create Vk buffer" ); + + if (allocationFlags & VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT) + data = map(); } VkBuffer::VkBuffer(VkBuffer&& o) noexcept : Buffer(o.bufferUsage, o.size), allocation(std::exchange(o.allocation, nullptr)), - buffer(std::exchange(o.buffer, nullptr)) {} + buffer(std::exchange(o.buffer, nullptr)), + data(std::exchange(o.data, nullptr)) {} VkBuffer::~VkBuffer() { + unmap(); vmaDestroyBuffer(device->getAllocator(), buffer, allocation); } - void VkBuffer::write(const char* data, const size_t dataSize, const size_t offset) { + void VkBuffer::write(const char* d, const size_t dataSize, const size_t offset) { + if (!data) + throw std::runtime_error("This buffer is not mapped and thus not writable"); if (offset + dataSize > size) throw std::runtime_error("Buffer overflow"); - void* d = map(); - memcpy(static_cast(d) + offset, data, dataSize); - unmap(); - } - - char* VkBuffer::map() { - void* data; - checkVulkanResult( - vmaMapMemory(device->getAllocator(), allocation, &data), - "Failed to map buffer" - ); - - return static_cast(data); - } - - void VkBuffer::unmap() { - vmaUnmapMemory(device->getAllocator(), allocation); + memcpy(data + offset, d, dataSize); } ::VkBuffer VkBuffer::getBuffer() const { @@ -138,4 +130,19 @@ namespace Vixen::Vk { return destination; } + + char* VkBuffer::map() { + void* data; + checkVulkanResult( + vmaMapMemory(device->getAllocator(), allocation, &data), + "Failed to map buffer" + ); + + return static_cast(data); + } + + void VkBuffer::unmap() { + if (data) + vmaUnmapMemory(device->getAllocator(), allocation); + } } diff --git a/src/engine/vk/VkBuffer.h b/src/engine/vk/VkBuffer.h index 6918aa9..de0e6fd 100644 --- a/src/engine/vk/VkBuffer.h +++ b/src/engine/vk/VkBuffer.h @@ -13,29 +13,27 @@ namespace Vixen::Vk { ::VkBuffer buffer; + char* data; + public: - VkBuffer(const std::shared_ptr &device, Usage bufferUsage, const size_t &size); + VkBuffer(const std::shared_ptr& device, Usage bufferUsage, const size_t& size); - VkBuffer(const VkBuffer &) = delete; + VkBuffer(const VkBuffer&) = delete; - VkBuffer &operator=(const VkBuffer &) = delete; + VkBuffer& operator=(const VkBuffer&) = delete; - VkBuffer(VkBuffer &&o) noexcept; + VkBuffer(VkBuffer&& o) noexcept; ~VkBuffer() override; - char *map() override; - - void unmap() override; - - void write(const char *data, size_t dataSize, size_t offset) override; + void write(const char* d, size_t dataSize, size_t offset) override; /** * Copies data from one buffer to another. * @param destination The destination buffer to copy the data to. * @param destinationOffset The offset from the destination buffer to copy into. */ - void transfer(VkBuffer &destination, size_t destinationOffset); + void transfer(VkBuffer& destination, size_t destinationOffset); [[nodiscard]] ::VkBuffer getBuffer() const; @@ -47,24 +45,29 @@ namespace Vixen::Vk { * @param data Pointer to the start of the data. * @return Returns the resulting device local buffer. */ - static VkBuffer stage(const std::shared_ptr &device, Usage usage, size_t size, const char *data); + static VkBuffer stage(const std::shared_ptr& device, Usage usage, size_t size, const char* data); - template + template static VkBuffer stage( - const std::shared_ptr &device, - const Usage usage, - const size_t size, - F lambda + const std::shared_ptr& device, + const Usage usage, + const size_t size, + F lambda ) { auto source = VkBuffer(device, usage | Usage::TRANSFER_SRC, size); auto destination = VkBuffer(device, usage | Usage::TRANSFER_DST, size); - void *data = source.map(); + void* data = source.map(); lambda(data); source.unmap(); source.transfer(destination, 0); return destination; } + + private: + char* map() override; + + void unmap() override; }; } diff --git a/src/engine/vk/VkDescriptorSet.cpp b/src/engine/vk/VkDescriptorSet.cpp index 0b3d7b3..66e4621 100644 --- a/src/engine/vk/VkDescriptorSet.cpp +++ b/src/engine/vk/VkDescriptorSet.cpp @@ -68,6 +68,29 @@ namespace Vixen::Vk { vkUpdateDescriptorSets(device->getDevice(), 1, &write, 0, nullptr); } + void VkDescriptorSet::updateCombinedImageSampler(const uint32_t binding, const VkSampler &sampler, const VkImageView &view) const { + const VkDescriptorImageInfo imageInfo{ + .sampler = sampler.getSampler(), + .imageView = view.getImageView(), + .imageLayout = view.getImage()->getLayout() + }; + + const VkWriteDescriptorSet write{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = set, + .dstBinding = binding, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &imageInfo, + .pBufferInfo = nullptr, + .pTexelBufferView = nullptr + }; + + vkUpdateDescriptorSets(device->getDevice(), 1, &write, 0, nullptr); + } + ::VkDescriptorSet VkDescriptorSet::getSet() const { return set; } diff --git a/src/engine/vk/VkDescriptorSet.h b/src/engine/vk/VkDescriptorSet.h index 78d2f42..46b6c4d 100644 --- a/src/engine/vk/VkDescriptorSet.h +++ b/src/engine/vk/VkDescriptorSet.h @@ -3,6 +3,8 @@ #include "Device.h" #include "VkDescriptorPool.h" #include "VkBuffer.h" +#include "VkSampler.h" +#include "VkImageView.h" namespace Vixen::Vk { class VkDescriptorSet { @@ -31,7 +33,13 @@ namespace Vixen::Vk { void updateUniformBuffer( uint32_t binding, - const VkBuffer &buffer + const VkBuffer& buffer + ) const; + + void updateCombinedImageSampler( + uint32_t binding, + const VkSampler &sampler, + const VkImageView &view ) const; [[nodiscard]] ::VkDescriptorSet getSet() const; diff --git a/src/engine/vk/VkFramebuffer.cpp b/src/engine/vk/VkFramebuffer.cpp index 8fc512a..3637e4f 100644 --- a/src/engine/vk/VkFramebuffer.cpp +++ b/src/engine/vk/VkFramebuffer.cpp @@ -2,96 +2,99 @@ namespace Vixen::Vk { VkFramebuffer::VkFramebuffer( - const std::shared_ptr &device, - const VkRenderPass &renderPass, - uint32_t width, - uint32_t height + const std::shared_ptr& device, + const VkRenderPass& renderPass, + uint32_t width, + uint32_t height ) : device(device), framebuffer(VK_NULL_HANDLE) { - const auto &attachments = renderPass.getAttachments(); + const auto& attachments = renderPass.getAttachments(); images.reserve(attachments.size()); imageViews.reserve(attachments.size()); for (size_t i = 0; i < attachments.size(); i++) { - const auto &attachment = attachments[i]; + const auto& [flags, format, samples, loadOp, storeOp, stencilLoadOp, stencilStoreOp, initialLayout, + finalLayout] = attachments[i]; - images.emplace_back( + images.push_back( + std::make_shared( device, width, height, VK_SAMPLE_COUNT_1_BIT, - attachment.format, + format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT + ) ); imageViews.emplace_back( - std::make_unique( - images[i], - VK_IMAGE_ASPECT_DEPTH_BIT - ) + std::make_unique( + images[i], + VK_IMAGE_ASPECT_DEPTH_BIT + ) ); } std::vector<::VkImageView> views{imageViews.size()}; - std::ranges::transform(imageViews, views.data(), [](const auto &imageView) { + std::ranges::transform(imageViews, views.data(), [](const auto& imageView) { return imageView->getImageView(); }); - VkFramebufferCreateInfo info{ - .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .renderPass = renderPass.getRenderPass(), - .attachmentCount = static_cast(views.size()), - .pAttachments = views.data(), - .width = width, - .height = height, - .layers = 0 + const VkFramebufferCreateInfo info{ + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = renderPass.getRenderPass(), + .attachmentCount = static_cast(views.size()), + .pAttachments = views.data(), + .width = width, + .height = height, + .layers = 0 }; checkVulkanResult( - vkCreateFramebuffer( - device->getDevice(), - &info, - nullptr, - &framebuffer - ), - "Failed to create framebuffer" + vkCreateFramebuffer( + device->getDevice(), + &info, + nullptr, + &framebuffer + ), + "Failed to create framebuffer" ); } VkFramebuffer::VkFramebuffer( - const std::shared_ptr &device, - const VkRenderPass &renderPass, - uint32_t width, - uint32_t height, - std::vector<::VkImageView> imageViews + const std::shared_ptr& device, + const VkRenderPass& renderPass, + uint32_t width, + uint32_t height, + std::vector<::VkImageView> imageViews ) : device(device), framebuffer(VK_NULL_HANDLE) { VkFramebufferCreateInfo info{ - .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .renderPass = renderPass.getRenderPass(), - .attachmentCount = static_cast(imageViews.size()), - .pAttachments = imageViews.data(), - .width = width, - .height = height, - .layers = 1 + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = renderPass.getRenderPass(), + .attachmentCount = static_cast(imageViews.size()), + .pAttachments = imageViews.data(), + .width = width, + .height = height, + .layers = 1 }; checkVulkanResult( - vkCreateFramebuffer( - device->getDevice(), - &info, - nullptr, - &framebuffer - ), - "Failed to create framebuffer" + vkCreateFramebuffer( + device->getDevice(), + &info, + nullptr, + &framebuffer + ), + "Failed to create framebuffer" ); } - VkFramebuffer::VkFramebuffer(VkFramebuffer &&o) noexcept - : device(std::move(o.device)), - images(std::exchange(o.images, {})), - imageViews(std::exchange(o.imageViews, {})), - framebuffer(std::exchange(o.framebuffer, nullptr)) {} + VkFramebuffer::VkFramebuffer(VkFramebuffer&& o) noexcept + : device(std::move(o.device)), + images(std::exchange(o.images, {})), + imageViews(std::exchange(o.imageViews, {})), + framebuffer(std::exchange(o.framebuffer, nullptr)) {} VkFramebuffer::~VkFramebuffer() { vkDestroyFramebuffer(device->getDevice(), framebuffer, nullptr); diff --git a/src/engine/vk/VkFramebuffer.h b/src/engine/vk/VkFramebuffer.h index 49ddb6d..67d803a 100644 --- a/src/engine/vk/VkFramebuffer.h +++ b/src/engine/vk/VkFramebuffer.h @@ -8,7 +8,7 @@ namespace Vixen::Vk { class VkFramebuffer { std::shared_ptr device; - std::vector images; + std::vector> images; std::vector> imageViews; diff --git a/src/engine/vk/VkImage.cpp b/src/engine/vk/VkImage.cpp index 8e3490d..e437740 100644 --- a/src/engine/vk/VkImage.cpp +++ b/src/engine/vk/VkImage.cpp @@ -2,13 +2,13 @@ namespace Vixen::Vk { VkImage::VkImage( - const std::shared_ptr &device, - uint32_t width, - uint32_t height, - VkSampleCountFlagBits samples, - VkFormat format, - VkImageTiling tiling, - VkImageUsageFlags usageFlags + const std::shared_ptr& device, + const uint32_t width, + const uint32_t height, + const VkSampleCountFlagBits samples, + const VkFormat format, + const VkImageTiling tiling, + const VkImageUsageFlags usageFlags ) : device(device), allocation(VK_NULL_HANDLE), image(VK_NULL_HANDLE), @@ -17,114 +17,219 @@ namespace Vixen::Vk { format(format), usageFlags(usageFlags), layout(VK_IMAGE_LAYOUT_UNDEFINED) { - VkImageCreateInfo imageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .imageType = VK_IMAGE_TYPE_2D, - .format = format, - .extent{ - .width = width, - .height = height, - .depth = 1 - }, - .mipLevels = 1, - .arrayLayers = 1, - .samples = samples, - .tiling = tiling, - .usage = usageFlags, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .initialLayout = layout + const VkImageCreateInfo imageCreateInfo{ + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = format, + .extent{ + .width = width, + .height = height, + .depth = 1 + }, + .mipLevels = 1, + .arrayLayers = 1, + .samples = samples, + .tiling = tiling, + .usage = usageFlags, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = layout }; - VmaAllocationCreateInfo allocationCreateInfo = { - .flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, - .usage = VMA_MEMORY_USAGE_AUTO, - .priority = 1.0f + constexpr VmaAllocationCreateInfo allocationCreateInfo = { + .flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, + .usage = VMA_MEMORY_USAGE_AUTO, + .priority = 1.0f }; checkVulkanResult( - vmaCreateImage( - device->getAllocator(), - &imageCreateInfo, - &allocationCreateInfo, - &image, - &allocation, - nullptr - ), - "Failed to create image" + vmaCreateImage( + device->getAllocator(), + &imageCreateInfo, + &allocationCreateInfo, + &image, + &allocation, + nullptr + ), + "Failed to create image" ); } - VkImage::VkImage(VkImage &&o) noexcept: - allocation(std::exchange(o.allocation, VK_NULL_HANDLE)), - width(o.width), - height(o.height), - layout(o.layout), - usageFlags(o.usageFlags), - device(o.device), - image(std::exchange(o.image, VK_NULL_HANDLE)), - format(o.format) {} + VkImage::VkImage(VkImage&& other) noexcept + : device(other.device), + allocation(std::exchange(other.allocation, VK_NULL_HANDLE)), + width(other.width), + height(other.height), + layout(other.layout), + usageFlags(other.usageFlags), + image(std::exchange(other.image, VK_NULL_HANDLE)), + format(other.format) {} + + VkImage const& VkImage::operator=(VkImage&& other) noexcept { + std::swap(device, other.device); + std::swap(allocation, other.allocation); + std::swap(width, other.width); + std::swap(height, other.height); + std::swap(layout, other.layout); + std::swap(usageFlags, other.usageFlags); + std::swap(image, other.image); + std::swap(format, other.format); + + return *this; + } VkImage::~VkImage() { vmaDestroyImage(device->getAllocator(), image, allocation); } - VkImage VkImage::from(const std::shared_ptr &device, const std::string &path) { - // FreeImage_Initialise(); - // - // const auto &format = FreeImage_GetFileType(path.c_str(), 0); - // if (format == FIF_UNKNOWN) - // error("Failed to determine image format, possibly unsupported format?"); - // - // const auto &bitmap = FreeImage_Load(format, path.c_str(), 0); - // if (!bitmap) - // error("Failed to load image from file \"{}\"", path); - // - // const auto &converted = FreeImage_ConvertTo32Bits(bitmap); - // - // uint32_t width = FreeImage_GetWidth(converted); - // uint32_t height = FreeImage_GetHeight(converted); - // auto pixels = FreeImage_GetBits(converted); - // - // VkDeviceSize size = width * height * sizeof(uint32_t); - // - // auto staging = VkBuffer(device, Buffer::Usage::INDEX, size); - // staging.write(reinterpret_cast(pixels), size, 0); - // - // FreeImage_Unload(converted); - // FreeImage_Unload(bitmap); - // - // FreeImage_DeInitialise(); - // - // auto image = VkImage( - // device, - // width, - // height, - // VK_SAMPLE_COUNT_1_BIT, - // VK_FORMAT_R8G8B8A8_SRGB, - // VK_IMAGE_TILING_OPTIMAL, - // VK_IMAGE_USAGE_SAMPLED_BIT - // ); - // - // image.transition(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - // image.copyFrom(staging); - // image.transition(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - // - // return image; + VkImage VkImage::from(const std::shared_ptr& device, const std::string& path) { + FreeImage_Initialise(); + + const auto& format = FreeImage_GetFileType(path.c_str(), 0); + if (format == FIF_UNKNOWN) + error("Failed to determine image format, possibly unsupported format?"); + + const auto& bitmap = FreeImage_Load(format, path.c_str(), 0); + if (!bitmap) + error("Failed to load image from file \"{}\"", path); + + const auto& converted = FreeImage_ConvertTo32Bits(bitmap); + + const auto& width = FreeImage_GetWidth(converted); + const auto& height = FreeImage_GetHeight(converted); + const auto& bitsPerPixel = FreeImage_GetBPP(converted); + const auto& pixels = FreeImage_GetBits(converted); + + const VkDeviceSize size = width * height * (bitsPerPixel / 8); + + auto staging = VkBuffer(device, Buffer::Usage::UNIFORM | Buffer::Usage::TRANSFER_SRC, size); + staging.write(reinterpret_cast(pixels), size, 0); + + FreeImage_Unload(converted); + FreeImage_Unload(bitmap); + + FreeImage_DeInitialise(); + + VkFormat f; + switch (bitsPerPixel) { + case 24: + f = VK_FORMAT_R8G8B8_SRGB; + break; + case 32: + f = VK_FORMAT_R8G8B8A8_SRGB; + break; + default: + throw std::runtime_error("Failed to determine format"); + } + + auto image = VkImage( + device, + width, + height, + VK_SAMPLE_COUNT_1_BIT, + f, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT + ); + + image.transition(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + image.copyFrom(staging); + image.transition(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + return image; } void VkImage::transition(VkImageLayout newLayout) { - throw std::runtime_error("Not implemented"); + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = layout; + 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; // TODO: This looks sus too + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = 0; + + VkPipelineStageFlags source; + VkPipelineStageFlags destination; + + if (layout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + source = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + destination = VK_PIPELINE_STAGE_TRANSFER_BIT; + } + else if (layout == 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; + + source = VK_PIPELINE_STAGE_TRANSFER_BIT; + destination = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + } + else { + throw std::runtime_error("Unsupported transition layout"); + } + + device->getTransferCommandPool() + ->allocateCommandBuffer(VkCommandBuffer::Level::PRIMARY) + .record( + VkCommandBuffer::Usage::SINGLE, + [this, &source, &destination, &barrier](auto commandBuffer) { + vkCmdPipelineBarrier( + commandBuffer, + source, + destination, + 0, + 0, + nullptr, + 0, + nullptr, + 1, + &barrier + ); + } + ) + .submit(device->getTransferQueue(), {}, {}, {}); + + layout = newLayout; } - void VkImage::copyFrom(VkBuffer const &buffer) { - throw std::runtime_error("Not implemented"); + void VkImage::copyFrom(VkBuffer const& buffer) { + 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}; + + device->getTransferCommandPool() + ->allocateCommandBuffer(VkCommandBuffer::Level::PRIMARY) + .record( + VkCommandBuffer::Usage::SINGLE, + [this, &buffer, ®ion](const auto& commandBuffer) { + vkCmdCopyBufferToImage(commandBuffer, buffer.getBuffer(), image, layout, 1, ®ion); + } + ) + .submit(device->getTransferQueue(), {}, {}, {}); } ::VkImage VkImage::getImage() const { return image; } - const std::shared_ptr &VkImage::getDevice() const { + const std::shared_ptr& VkImage::getDevice() const { return device; } @@ -132,6 +237,10 @@ namespace Vixen::Vk { return format; } + VkImageLayout VkImage::getLayout() const { + return layout; + } + VkImageUsageFlags VkImage::getUsageFlags() const { return usageFlags; } diff --git a/src/engine/vk/VkImage.h b/src/engine/vk/VkImage.h index 0e7613b..5cbeef6 100644 --- a/src/engine/vk/VkImage.h +++ b/src/engine/vk/VkImage.h @@ -1,12 +1,12 @@ #pragma once -#include +#include #include "Device.h" #include "VkBuffer.h" namespace Vixen::Vk { class VkImage { - const std::shared_ptr device; + std::shared_ptr device; VmaAllocation allocation; @@ -24,33 +24,39 @@ namespace Vixen::Vk { public: VkImage( - const std::shared_ptr &device, - uint32_t width, - uint32_t height, - VkSampleCountFlagBits samples, - VkFormat format, - VkImageTiling tiling, - VkImageUsageFlags usageFlags + const std::shared_ptr& device, + uint32_t width, + uint32_t height, + VkSampleCountFlagBits samples, + VkFormat format, + VkImageTiling tiling, + VkImageUsageFlags usageFlags ); - VkImage(const VkImage &) = delete; + VkImage(VkImage& other) = delete; - VkImage(VkImage &&o) noexcept; + VkImage& operator=(const VkImage& other) = delete; + + VkImage(VkImage&& other) noexcept; + + VkImage const& operator=(VkImage&& other) noexcept; ~VkImage(); - static VkImage from(const std::shared_ptr &device, const std::string &path); + static VkImage from(const std::shared_ptr& device, const std::string& path); void transition(VkImageLayout newLayout); - void copyFrom(const VkBuffer &buffer); + void copyFrom(const VkBuffer& buffer); [[nodiscard]] VkFormat getFormat() const; + [[nodiscard]] VkImageLayout getLayout() const; + [[nodiscard]] VkImageUsageFlags getUsageFlags() const; [[nodiscard]] ::VkImage getImage() const; - [[nodiscard]] const std::shared_ptr &getDevice() const; + [[nodiscard]] const std::shared_ptr& getDevice() const; }; } diff --git a/src/engine/vk/VkImageView.cpp b/src/engine/vk/VkImageView.cpp index 431cef0..6e559c9 100644 --- a/src/engine/vk/VkImageView.cpp +++ b/src/engine/vk/VkImageView.cpp @@ -1,14 +1,14 @@ #include "VkImageView.h" namespace Vixen::Vk { - VkImageView::VkImageView(const VkImage &image, VkImageAspectFlags aspectFlags) - : image(image), - imageView(VK_NULL_HANDLE) { + VkImageView::VkImageView(const std::shared_ptr& image, VkImageAspectFlags aspectFlags) + : image(image), + imageView(VK_NULL_HANDLE) { VkImageViewCreateInfo imageViewCreateInfo{}; imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - imageViewCreateInfo.image = image.getImage(); + imageViewCreateInfo.image = image->getImage(); imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageViewCreateInfo.format = image.getFormat(); + imageViewCreateInfo.format = image->getFormat(); imageViewCreateInfo.subresourceRange.aspectMask = aspectFlags; imageViewCreateInfo.subresourceRange.baseMipLevel = 0; imageViewCreateInfo.subresourceRange.levelCount = 1; @@ -16,24 +16,28 @@ namespace Vixen::Vk { imageViewCreateInfo.subresourceRange.layerCount = 1; checkVulkanResult( - vkCreateImageView(image.getDevice()->getDevice(), &imageViewCreateInfo, nullptr, &imageView), - "Failed to create image view" + vkCreateImageView(image->getDevice()->getDevice(), &imageViewCreateInfo, nullptr, &imageView), + "Failed to create image view" ); } -// VkImageView::VkImageView(VkImageView &&o) noexcept -// : image(o.image), -// imageView(std::exchange(o.imageView, VK_NULL_HANDLE)) {} + // VkImageView::VkImageView(VkImageView &&o) noexcept + // : image(o.image), + // imageView(std::exchange(o.imageView, VK_NULL_HANDLE)) {} VkImageView::~VkImageView() { vkDestroyImageView( - image.getDevice()->getDevice(), - imageView, - nullptr + image->getDevice()->getDevice(), + imageView, + nullptr ); } ::VkImageView VkImageView::getImageView() const { return imageView; } + + std::shared_ptr VkImageView::getImage() const { + return image; + } } diff --git a/src/engine/vk/VkImageView.h b/src/engine/vk/VkImageView.h index 0e40932..09cb700 100644 --- a/src/engine/vk/VkImageView.h +++ b/src/engine/vk/VkImageView.h @@ -4,12 +4,12 @@ namespace Vixen::Vk { class VkImageView { - const VkImage ℑ + std::shared_ptr image; ::VkImageView imageView; public: - VkImageView(const VkImage &image, VkImageAspectFlags aspectFlags); + VkImageView(const std::shared_ptr &image, VkImageAspectFlags aspectFlags); VkImageView(const VkImageView &) = delete; @@ -20,5 +20,7 @@ namespace Vixen::Vk { ~VkImageView(); [[nodiscard]] ::VkImageView getImageView() const; + + [[nodiscard]] std::shared_ptr getImage() const; }; } diff --git a/src/engine/vk/VkPipelineLayout.cpp b/src/engine/vk/VkPipelineLayout.cpp index e529d5e..6059f78 100644 --- a/src/engine/vk/VkPipelineLayout.cpp +++ b/src/engine/vk/VkPipelineLayout.cpp @@ -3,13 +3,14 @@ namespace Vixen::Vk { VkPipelineLayout::VkPipelineLayout(const std::shared_ptr& device, const VkShaderProgram& program) : device(device) { - const auto& l = program.getVertex()->getDescriptorSetLayout().getLayout(); + std::vector<::VkDescriptorSetLayout> layouts; + layouts.push_back(program.getDescriptorSetLayout()->getLayout()); const VkPipelineLayoutCreateInfo pipelineLayoutInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .pNext = nullptr, .flags = 0, - .setLayoutCount = 1, - .pSetLayouts = &l, + .setLayoutCount = static_cast(layouts.size()), + .pSetLayouts = layouts.data(), .pushConstantRangeCount = 0, .pPushConstantRanges = nullptr }; diff --git a/src/engine/vk/VkRenderer.cpp b/src/engine/vk/VkRenderer.cpp index e79de12..3c6343e 100644 --- a/src/engine/vk/VkRenderer.cpp +++ b/src/engine/vk/VkRenderer.cpp @@ -37,18 +37,18 @@ namespace Vixen::Vk { const VkBuffer& buffer, uint32_t vertexCount, uint32_t indexCount, - const VkDescriptorSet& set + const std::vector<::VkDescriptorSet>& descriptorSets ) { - auto state = swapchain.acquireImage( + if (const auto state = swapchain.acquireImage( std::numeric_limits::max(), - [this, &buffer, &vertexCount, &indexCount, &set]( + [this, &buffer, &vertexCount, &indexCount, &descriptorSets]( const auto& currentFrame, const auto& imageIndex, const auto& imageAvailableSemaphore ) { auto& commandBuffer = renderCommandBuffers[currentFrame]; - prepare(commandBuffer, framebuffers[imageIndex], buffer, vertexCount, indexCount, set); + prepare(commandBuffer, framebuffers[imageIndex], buffer, vertexCount, indexCount, descriptorSets); std::vector<::VkSemaphore> waitSemaphores = {imageAvailableSemaphore.getSemaphore()}; std::vector<::VkSemaphore> signalSemaphores = { @@ -64,9 +64,7 @@ namespace Vixen::Vk { swapchain.present(imageIndex, signalSemaphores); } - ); - - if (state == Swapchain::State::OUT_OF_DATE) { + ); state == Swapchain::State::OUT_OF_DATE) { device->waitIdle(); framebuffers.clear(); @@ -85,11 +83,11 @@ namespace Vixen::Vk { const VkBuffer& buffer, uint32_t vertexCount, uint32_t indexCount, - const VkDescriptorSet& set + const std::vector<::VkDescriptorSet>& descriptorSets ) const { commandBuffer.record( VkCommandBuffer::Usage::SIMULTANEOUS, - [this, &framebuffer, &buffer, &vertexCount, &indexCount, &set](::VkCommandBuffer commandBuffer) { + [this, &framebuffer, &buffer, &vertexCount, &indexCount, &descriptorSets](::VkCommandBuffer commandBuffer) { std::vector clearValues{ { .color = { @@ -162,14 +160,13 @@ namespace Vixen::Vk { VK_INDEX_TYPE_UINT32 ); - const auto& s = set.getSet(); vkCmdBindDescriptorSets( commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout->getLayout(), 0, - 1, - &s, + descriptorSets.size(), + descriptorSets.data(), 0, nullptr ); @@ -189,14 +186,16 @@ namespace Vixen::Vk { depthImageViews.reserve(imageCount); framebuffers.reserve(imageCount); for (size_t i = 0; i < imageCount; i++) { - depthImages.emplace_back( - device, - swapchain.getExtent().width, - swapchain.getExtent().height, - VK_SAMPLE_COUNT_1_BIT, - VK_FORMAT_D32_SFLOAT, - VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT + depthImages.push_back( + std::make_shared( + device, + swapchain.getExtent().width, + swapchain.getExtent().height, + VK_SAMPLE_COUNT_1_BIT, + VK_FORMAT_D32_SFLOAT, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT + ) ); depthImageViews.emplace_back( diff --git a/src/engine/vk/VkRenderer.h b/src/engine/vk/VkRenderer.h index 5cff7f6..630143f 100644 --- a/src/engine/vk/VkRenderer.h +++ b/src/engine/vk/VkRenderer.h @@ -22,7 +22,7 @@ namespace Vixen::Vk { std::vector renderCommandBuffers; - std::vector depthImages; + std::vector> depthImages; std::vector> depthImageViews; @@ -47,7 +47,7 @@ namespace Vixen::Vk { const VkBuffer& buffer, uint32_t vertexCount, uint32_t indexCount, - const VkDescriptorSet& set + const std::vector<::VkDescriptorSet> &descriptorSets ); private: @@ -59,7 +59,7 @@ namespace Vixen::Vk { const VkBuffer& buffer, uint32_t vertexCount, uint32_t indexCount, - const VkDescriptorSet& set + const std::vector<::VkDescriptorSet> &descriptorSets ) const; }; } diff --git a/src/engine/vk/VkSampler.cpp b/src/engine/vk/VkSampler.cpp new file mode 100644 index 0000000..fcc531e --- /dev/null +++ b/src/engine/vk/VkSampler.cpp @@ -0,0 +1,55 @@ +#include "VkSampler.h" + +namespace Vixen::Vk { + VkSampler::VkSampler(const std::shared_ptr& device, VkSamplerCreateInfo info) + : device(device), + sampler(VK_NULL_HANDLE) { + checkVulkanResult( + vkCreateSampler(device->getDevice(), &info, nullptr, &sampler), + "Failed to create sampler" + ); + } + + VkSampler::VkSampler(const std::shared_ptr& device) : VkSampler( + device, + { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .mipLodBias = 0.0f, + .anisotropyEnable = VK_FALSE, + .maxAnisotropy = 1, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_ALWAYS, + .minLod = 0.0f, + .maxLod = 0.0f, + .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE + } + ) {} + + VkSampler::VkSampler(VkSampler&& other) noexcept + : device(std::exchange(other.device, nullptr)), + sampler(std::exchange(other.sampler, nullptr)) {} + + VkSampler const& VkSampler::operator=(VkSampler&& other) noexcept { + std::swap(device, other.device); + std::swap(sampler, other.sampler); + + return *this; + } + + VkSampler::~VkSampler() { + vkDestroySampler(device->getDevice(), sampler, nullptr); + } + + ::VkSampler VkSampler::getSampler() const { + return sampler; + } +} diff --git a/src/engine/vk/VkSampler.h b/src/engine/vk/VkSampler.h new file mode 100644 index 0000000..73efd5b --- /dev/null +++ b/src/engine/vk/VkSampler.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Device.h" + +namespace Vixen::Vk { + class VkSampler { + std::shared_ptr device; + + ::VkSampler sampler; + + public: + VkSampler(const std::shared_ptr& device, VkSamplerCreateInfo info); + + explicit VkSampler(const std::shared_ptr& device); + + VkSampler(VkSampler& other) = delete; + + VkSampler& operator=(const VkSampler& other) = delete; + + VkSampler(VkSampler&& other) noexcept; + + VkSampler const& operator=(VkSampler&& other) noexcept; + + ~VkSampler(); + + [[nodiscard]] ::VkSampler getSampler() const; + }; +} diff --git a/src/engine/vk/VkShaderModule.cpp b/src/engine/vk/VkShaderModule.cpp index 726e0d5..48debdf 100644 --- a/src/engine/vk/VkShaderModule.cpp +++ b/src/engine/vk/VkShaderModule.cpp @@ -10,9 +10,8 @@ namespace Vixen::Vk { const std::vector& uniformBuffers, const std::string& entrypoint ) : ShaderModule(stage, entrypoint, bindings, inputs, uniformBuffers), - device(device), module(VK_NULL_HANDLE), - descriptorSetLayout(device, createBindings()) { + device(device) { const VkShaderModuleCreateInfo info{ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = binary.size() * sizeof(uint32_t), @@ -75,7 +74,5 @@ namespace Vixen::Vk { return b; } - const VkDescriptorSetLayout& VkShaderModule::getDescriptorSetLayout() { - return descriptorSetLayout; - } + std::shared_ptr VkShaderModule::getDevice() const { return device; } } diff --git a/src/engine/vk/VkShaderModule.h b/src/engine/vk/VkShaderModule.h index 1fd53f1..0710416 100644 --- a/src/engine/vk/VkShaderModule.h +++ b/src/engine/vk/VkShaderModule.h @@ -17,7 +17,6 @@ #include "../ShaderModule.h" #include "Vulkan.h" #include "Device.h" -#include "VkDescriptorSetLayout.h" #define VIXEN_VK_SPIRV_VERSION 130 @@ -27,8 +26,6 @@ namespace Vixen::Vk { std::shared_ptr device; - VkDescriptorSetLayout descriptorSetLayout; - public: VkShaderModule( const std::shared_ptr& device, @@ -50,7 +47,7 @@ namespace Vixen::Vk { [[nodiscard]] std::vector createBindings() const; - [[nodiscard]] const VkDescriptorSetLayout& getDescriptorSetLayout(); + [[nodiscard]] std::shared_ptr getDevice() const; class Builder { // TODO: This builder has slightly confusing API, you can compile before setting the stage meaning its possible to mistakenly have the wrong stage set at compile time @@ -120,6 +117,7 @@ namespace Vixen::Vk { shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_3); shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_6); shader.setAutoMapLocations(true); + shader.setAutoMapBindings(true); shader.setEntryPoint(entrypoint.c_str()); shader.setSourceEntryPoint(entrypoint.c_str()); diff --git a/src/engine/vk/VkShaderProgram.cpp b/src/engine/vk/VkShaderProgram.cpp index 57e729d..8eb25d7 100644 --- a/src/engine/vk/VkShaderProgram.cpp +++ b/src/engine/vk/VkShaderProgram.cpp @@ -2,7 +2,21 @@ namespace Vixen::Vk { VkShaderProgram::VkShaderProgram( - const std::shared_ptr &vertex, - const std::shared_ptr &fragment - ) : ShaderProgram(vertex, fragment) {} + const std::shared_ptr& vertex, + const std::shared_ptr& fragment + ) : ShaderProgram(vertex, fragment), + descriptorSetLayout(nullptr) { + const auto& vertexBindings = vertex->createBindings(); + const auto& fragmentBindings = fragment->createBindings(); + + std::vector bindings{}; + bindings.insert(bindings.end(), vertexBindings.begin(), vertexBindings.end()); + bindings.insert(bindings.end(), fragmentBindings.begin(), fragmentBindings.end()); + + descriptorSetLayout = std::make_shared(vertex->getDevice(), bindings); + } + + std::shared_ptr VkShaderProgram::getDescriptorSetLayout() const { + return descriptorSetLayout; + } } diff --git a/src/engine/vk/VkShaderProgram.h b/src/engine/vk/VkShaderProgram.h index 6c3aeef..1e33d75 100644 --- a/src/engine/vk/VkShaderProgram.h +++ b/src/engine/vk/VkShaderProgram.h @@ -2,10 +2,18 @@ #include "../ShaderProgram.h" #include "VkShaderModule.h" +#include "VkDescriptorSetLayout.h" namespace Vixen::Vk { class VkShaderProgram : public ShaderProgram { + std::shared_ptr descriptorSetLayout; + public: - VkShaderProgram(const std::shared_ptr &vertex, const std::shared_ptr &fragment); + VkShaderProgram( + const std::shared_ptr& vertex, + const std::shared_ptr& fragment + ); + + [[nodiscard]] std::shared_ptr getDescriptorSetLayout() const; }; } diff --git a/src/engine/vk/test/main.cpp b/src/engine/vk/test/main.cpp index 7d26168..24bed9c 100644 --- a/src/engine/vk/test/main.cpp +++ b/src/engine/vk/test/main.cpp @@ -94,7 +94,7 @@ int main() { Vixen::Buffer::Usage::INDEX, vertices.size() * sizeof(Vertex) + indices.size() * sizeof(uint32_t), - [&vertices, &indices](const auto &data) { + [&vertices, &indices](const auto& data) { memcpy( data, vertices.data(), @@ -115,6 +115,10 @@ int main() { { .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount = 1 + }, + { + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1 } }; @@ -130,9 +134,19 @@ int main() { camera.perspective(static_cast(width) / static_cast(height)) }; - auto descriptorPool = std::make_shared(vixen.device, sizes, 1); - auto descriptorSet = Vixen::Vk::VkDescriptorSet(vixen.device, descriptorPool, vertex->getDescriptorSetLayout()); - descriptorSet.updateUniformBuffer(0, uniformBuffer); + auto descriptorPool = std::make_shared(vixen.device, sizes, 2); + auto mvp = Vixen::Vk::VkDescriptorSet(vixen.device, descriptorPool, *program.getDescriptorSetLayout()); + mvp.updateUniformBuffer(0, uniformBuffer); + + auto image = std::make_shared( + Vixen::Vk::VkImage::from(vixen.device, "texture.jpg")); + auto view = Vixen::Vk::VkImageView(image, VK_IMAGE_ASPECT_COLOR_BIT); + auto sampler = Vixen::Vk::VkSampler(vixen.device); + + //auto albedo = Vixen::Vk::VkDescriptorSet(vixen.device, descriptorPool, *program.getDescriptorSetLayout()); + mvp.updateCombinedImageSampler(1, sampler, view); + + const std::vector descriptorSets = {mvp.getSet()}; double old = glfwGetTime(); double lastFrame = old; @@ -147,10 +161,11 @@ int main() { const double& now = glfwGetTime(); double deltaTime = now - lastFrame; lastFrame = now; - ubo.model = glm::rotate(ubo.model, static_cast(deltaTime) * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.model = glm::rotate(ubo.model, static_cast(deltaTime) * glm::radians(90.0f), + glm::vec3(0.0f, 0.0f, 1.0f)); uniformBuffer.write(reinterpret_cast(&ubo), sizeof(UniformBufferObject), 0); - renderer->render(buffer, vertices.size(), indices.size(), descriptorSet); + renderer->render(buffer, vertices.size(), indices.size(), descriptorSets); fps++; if (now - old >= 1) {