From e077eeaf5d392c8338af87e9cfadd6a7b73aa189 Mon Sep 17 00:00:00 2001 From: connellpaxton Date: Tue, 23 Jan 2024 16:00:15 -0500 Subject: [PATCH] Added class to handle allocation and creation of Buffers and Image, as well as Timers. No memory leaks detected yet. (Fingers crossed). --- Memory/Buffer.cpp | 30 +++++++++++++++++++++++++ Memory/Buffer.hpp | 27 ++++++++++++++++++++++ Memory/Image.cpp | 44 ++++++++++++++++++++++++++++++++++++ Memory/Image.hpp | 20 +++++++++++++++++ Renderer/CommandBuffer.cpp | 25 +++++++++++++++++++-- Renderer/CommandBuffer.hpp | 5 +++++ Renderer/Pipeline.cpp | 19 ---------------- Renderer/Renderer.cpp | 7 +++--- Renderer/Swapchain.cpp | 40 +++++---------------------------- Renderer/Swapchain.hpp | 4 ++-- Resources/Texture.cpp | 46 ++++++++++++++------------------------ Resources/Texture.hpp | 10 +++++---- pléascach.cpp | 4 ++++ util/Timer.hpp | 38 +++++++++++++++++++++++++++++++ util/log.hpp | 2 +- 15 files changed, 226 insertions(+), 95 deletions(-) create mode 100644 Memory/Buffer.cpp create mode 100644 Memory/Buffer.hpp create mode 100644 Memory/Image.cpp create mode 100644 Memory/Image.hpp delete mode 100644 Renderer/Pipeline.cpp create mode 100644 util/Timer.hpp diff --git a/Memory/Buffer.cpp b/Memory/Buffer.cpp new file mode 100644 index 0000000..8cbeac5 --- /dev/null +++ b/Memory/Buffer.cpp @@ -0,0 +1,30 @@ +#include +#include + +Buffer::Buffer(vk::PhysicalDevice phys_dev, vk::Device dev, vk::DeviceSize sz, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags mem_flags, vk::SharingMode sharing) : dev(dev), size(sz) { + auto info = vk::BufferCreateInfo { + .size = sz, + .usage = usage, + .sharingMode = sharing, + }; + buffer = dev.createBuffer(info); + + auto reqs = dev.getBufferMemoryRequirements(buffer); + auto alloc_info = vk::MemoryAllocateInfo{ + .allocationSize = reqs.size, + .memoryTypeIndex = mem::choose_heap(phys_dev, reqs, mem_flags), + }; + memory = dev.allocateMemory(alloc_info); +} + +void Buffer::map(const uint8_t* data, vk::DeviceSize size) { + auto p = dev.mapMemory(memory, 0, size); + std::memcpy(p, data, size); + dev.unmapMemory(memory); +} + +Buffer::~Buffer() { + dev.freeMemory(memory); + dev.destroyBuffer(buffer); +} + diff --git a/Memory/Buffer.hpp b/Memory/Buffer.hpp new file mode 100644 index 0000000..e254435 --- /dev/null +++ b/Memory/Buffer.hpp @@ -0,0 +1,27 @@ +#pragma once + +#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS +#include + +struct Buffer { + Buffer(vk::PhysicalDevice phys_dev, vk::Device dev, vk::DeviceSize sz, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags mem_flags, vk::SharingMode sharing = vk::SharingMode::eExclusive); + + vk::DeviceSize size; + vk::Device dev; + vk::DeviceMemory memory; + vk::Buffer buffer; + + void map(const uint8_t* data, vk::DeviceSize size); + inline void map(const uint8_t* data) { + map(data, size); + } + inline void map(const std::vector& data) { + map(data.data(), size); + } + + operator vk::Buffer& () { + return buffer; + } + + ~Buffer(); +}; diff --git a/Memory/Image.cpp b/Memory/Image.cpp new file mode 100644 index 0000000..116f66d --- /dev/null +++ b/Memory/Image.cpp @@ -0,0 +1,44 @@ +#include +#include + + +Image::Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::Image _image, vk::MemoryPropertyFlags memory_flags) : dev(dev), image(_image) { + + auto reqs = dev.getImageMemoryRequirements(image); + + auto alloc = vk::MemoryAllocateInfo{ + .allocationSize = reqs.size, + .memoryTypeIndex = mem::choose_heap(phys_dev, reqs, memory_flags), + }; + memory = dev.allocateMemory(alloc); + dev.bindImageMemory(image, memory, 0); + +} + +Image::Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::ImageCreateInfo info, vk::MemoryPropertyFlags memory_flags) : dev(dev) { + image = dev.createImage(info); + + *this = Image(phys_dev, dev, image, memory_flags); +} + +Image::Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::Extent3D extent, vk::Format format, vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags memory_flags) : dev(dev) { + auto info = vk::ImageCreateInfo{ + .imageType = vk::ImageType::e2D, + .format = format, + .extent = extent, + .mipLevels = 1, + .arrayLayers = 1, + .samples = vk::SampleCountFlagBits::e1, + .tiling = tiling, + .usage = usage, + .sharingMode = vk::SharingMode::eExclusive, + .initialLayout = vk::ImageLayout::eUndefined, + }; + + *this = Image(phys_dev, dev, info, memory_flags); +} + +void Image::cleanup() { + dev.destroyImage(image); + dev.freeMemory(memory); +} \ No newline at end of file diff --git a/Memory/Image.hpp b/Memory/Image.hpp new file mode 100644 index 0000000..8510d93 --- /dev/null +++ b/Memory/Image.hpp @@ -0,0 +1,20 @@ +#pragma once + +#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS +#include + +#include + +struct Image { + vk::Device dev; + vk::Extent3D extent; + vk::Format format; + vk::Image image; + vk::DeviceMemory memory; + + Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::Image image, vk::MemoryPropertyFlags memory_flags); + Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::ImageCreateInfo info, vk::MemoryPropertyFlags memory_flags); + Image(vk::PhysicalDevice phys_dev, vk::Device dev, vk::Extent3D extent, vk::Format format, vk::ImageTiling, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags memory_flags); + + void cleanup(); +}; diff --git a/Renderer/CommandBuffer.cpp b/Renderer/CommandBuffer.cpp index c6e5550..8318344 100644 --- a/Renderer/CommandBuffer.cpp +++ b/Renderer/CommandBuffer.cpp @@ -1,6 +1,8 @@ #include #include +#include +#include #include @@ -32,8 +34,27 @@ void CommandBuffer::begin() { command_buffer.begin(begin_info); } -void CommandBuffer::copy(vk::Buffer in, vk::Buffer out, vk::ArrayProxy regions) { - command_buffer.copyBuffer(in, out, regions); +void CommandBuffer::copy(vk::Buffer src, vk::Buffer dst, vk::ArrayProxy regions) { + command_buffer.copyBuffer(src, dst, regions); +} + +void CommandBuffer::copy(Buffer& src, Image& dst, vk::ImageLayout layout) { + auto region = vk::BufferImageCopy{ + .bufferOffset = 0, + /* RowLength and ImageHeight are automatically set to imageExtent dimensions if set to 0 */ + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 0, + }, + .imageOffset = { 0, 0, 0 }, + .imageExtent = dst.extent, + }; + + command_buffer.copyBufferToImage(src, dst.image, layout, region); } void CommandBuffer::bind(const GraphicsPipeline& pipeline) { diff --git a/Renderer/CommandBuffer.hpp b/Renderer/CommandBuffer.hpp index 96e4b7a..a24a7d3 100644 --- a/Renderer/CommandBuffer.hpp +++ b/Renderer/CommandBuffer.hpp @@ -5,6 +5,9 @@ #include + +struct Buffer; +struct Image; struct GraphicsPipeline; struct ComputePipeline; @@ -18,6 +21,8 @@ struct CommandBuffer { /* copy between buffer */ void copy(vk::Buffer in, vk::Buffer out, vk::ArrayProxy regions); + /* copy buffer to image */ + void copy(Buffer& in, Image& out, vk::ImageLayout layout = vk::ImageLayout::eTransferDstOptimal); void bind(const GraphicsPipeline& pipeline); void bind(const ComputePipeline& pipeline); diff --git a/Renderer/Pipeline.cpp b/Renderer/Pipeline.cpp deleted file mode 100644 index 7f3bcd6..0000000 --- a/Renderer/Pipeline.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include - -#include - -ComputePipeline::ComputePipeline(vk::Device dev, const Shader& shader) { - auto shader_info = vk::PipelineShaderStageCreateInfo { - .stage = vk::ShaderStageFlagBits::eCompute, - .module = shader, - .pName = "main", - }; - - auto layout = vk::PipelineLayoutCreateInfo { - - }; - - auto create_info = vk::ComputePipelineCreateInfo { - .stage = shader_info, - }; -} \ No newline at end of file diff --git a/Renderer/Renderer.cpp b/Renderer/Renderer.cpp index 3bb17ea..1b378fd 100644 --- a/Renderer/Renderer.cpp +++ b/Renderer/Renderer.cpp @@ -5,15 +5,16 @@ #include +#include using namespace std::string_literals; Renderer::Renderer(Window& win) : win(win) { /* Create Instance object */ auto app_info = vk::ApplicationInfo { - .pApplicationName = "Pl�ascach Demo", + .pApplicationName = "Pléascach Demo", .applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), - .pEngineName = "Pl�ascach", + .pEngineName = "Pléascach", .engineVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), .apiVersion = VK_API_VERSION_1_1, }; @@ -162,7 +163,6 @@ Renderer::Renderer(Window& win) : win(win) { } void Renderer::draw() { - Log::debug("draw() called \n"); if(dev.waitForFences(render_fence, true, UINT64_MAX) != vk::Result::eSuccess) { Log::error("Failed to wait for fences in draw()\n"); @@ -240,7 +240,6 @@ void Renderer::draw() { } void Renderer::present() { - Log::debug("present() called \n"); auto present_info = vk::PresentInfoKHR{ .waitSemaphoreCount = 1, .pWaitSemaphores = &render_wait_semaphore, diff --git a/Renderer/Swapchain.cpp b/Renderer/Swapchain.cpp index 9590cb2..974b4c6 100644 --- a/Renderer/Swapchain.cpp +++ b/Renderer/Swapchain.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -66,38 +67,11 @@ void Swapchain::create(vk::SwapchainKHR old_swapchain) { images = dev.getSwapchainImagesKHR(swapchain); - auto depth_image_info = vk::ImageCreateInfo{ - .imageType = vk::ImageType::e2D, - .format = vk::Format::eD16Unorm, - .extent = { - .width = extent.width, - .height = extent.height, - .depth = 1, - }, - .mipLevels = 1, - .arrayLayers = 1, - .samples = vk::SampleCountFlagBits::e1, - .usage = vk::ImageUsageFlagBits::eDepthStencilAttachment, - .sharingMode = vk::SharingMode::eExclusive, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = NULL, - .initialLayout = vk::ImageLayout::eUndefined, - }; + depth_image = std::make_unique(phys_dev, dev, vk::Extent3D { extent.width, extent.height, 1 }, vk::Format::eD16Unorm, + vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal); - depth_image = dev.createImage(depth_image_info); - - auto depth_mem_reqs = dev.getImageMemoryRequirements(depth_image); - - auto depth_alloc_info = vk::MemoryAllocateInfo{ - .allocationSize = depth_mem_reqs.size, - .memoryTypeIndex = mem::choose_heap(phys_dev, depth_mem_reqs, vk::MemoryPropertyFlagBits::eDeviceLocal), - }; - - depth_alloc = dev.allocateMemory(depth_alloc_info); - dev.bindImageMemory(depth_image, depth_alloc, 0); - - auto depth_view_info = vk::ImageViewCreateInfo{ - .image = depth_image, + auto depth_view_info = vk::ImageViewCreateInfo { + .image = depth_image->image, .viewType = vk::ImageViewType::e2D, .format = vk::Format::eD16Unorm, .components = { @@ -152,7 +126,6 @@ void Swapchain::create(vk::SwapchainKHR old_swapchain) { framebuffers[i] = dev.createFramebuffer(framebuffer_info); } - } @@ -172,8 +145,7 @@ void Swapchain::cleanup() { dev.destroyImageView(view); dev.destroyImageView(depth_image_view); - dev.destroyImage(depth_image); - dev.freeMemory(depth_alloc); + depth_image->cleanup(); } Swapchain::~Swapchain() { diff --git a/Renderer/Swapchain.hpp b/Renderer/Swapchain.hpp index c3f3bc6..bf87da6 100644 --- a/Renderer/Swapchain.hpp +++ b/Renderer/Swapchain.hpp @@ -7,6 +7,7 @@ #include struct Window; +struct Image; struct Swapchain { Swapchain(Window& win, vk::Device dev, vk::PhysicalDevice phys_dev, const vk::SurfaceKHR& surface, RenderPass render_pass); @@ -34,9 +35,8 @@ struct Swapchain { std::vector views; std::vector framebuffers; - vk::Image depth_image; + std::unique_ptr depth_image; vk::ImageView depth_image_view; - vk::DeviceMemory depth_alloc; vk::Format format; diff --git a/Resources/Texture.cpp b/Resources/Texture.cpp index a0e415f..fe8e0ce 100644 --- a/Resources/Texture.cpp +++ b/Resources/Texture.cpp @@ -1,46 +1,34 @@ #include +#include #include +#include + #include #define STB_IMAGE_IMPLEMENTATION #include -Texture::Texture(vk::Device dev, vk::PhysicalDevice phys_dev, const std::string& fname) { +Texture::Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer command_buffer, const std::string& fname) { + int n_channels; vk::Extent3D extent; - auto image_data = stbi_load(fname.c_str(), reinterpret_cast(&extent.width), reinterpret_cast(&extent.height), NULL, NULL); + + auto image_data = stbi_load(fname.c_str(), reinterpret_cast(&extent.width), reinterpret_cast(&extent.height), &n_channels, STBI_rgb_alpha); extent.depth = 1; - auto image_info = vk::ImageCreateInfo{ - .imageType = vk::ImageType::e2D, - .format = vk::Format::eR8G8B8A8Unorm, - .extent = extent, - .mipLevels = 1, - .arrayLayers = 1, - .samples = vk::SampleCountFlagBits::e1, - .tiling = vk::ImageTiling::eOptimal, - .usage = vk::ImageUsageFlagBits::eSampled, - .sharingMode = vk::SharingMode::eExclusive, - .initialLayout = vk::ImageLayout::eUndefined, - }; + image = std::make_unique(phys_dev, dev, extent, vk::Format::eR8G8B8A8Srgb, + vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eSampled, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); - image = dev.createImage(image_info); + vk::DeviceSize sz = extent.width * extent.height * sizeof(uint32_t); - auto reqs = dev.getImageMemoryRequirements(image); + /* staging buffer to hold image data from the CPU */ + Buffer staging(phys_dev, dev, sz, vk::BufferUsageFlagBits::eTransferSrc, + vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); + staging.map(image_data); + stbi_image_free(image_data); - auto image_alloc_info = vk::MemoryAllocateInfo{ - .allocationSize = reqs.size, - .memoryTypeIndex = mem::choose_heap(phys_dev, reqs, vk::MemoryPropertyFlagBits::eHostVisible), - }; - - image_alloc = dev.allocateMemory(image_alloc_info); - dev.bindImageMemory(image, image_alloc, 0); - - /* TODO: Copy memory into image using buffers */ -} - -void Texture::cleanup(vk::Device dev) { - dev.freeMemory(image_alloc); + command_buffer.copy(staging, *image); } \ No newline at end of file diff --git a/Resources/Texture.hpp b/Resources/Texture.hpp index a3072e0..d163325 100644 --- a/Resources/Texture.hpp +++ b/Resources/Texture.hpp @@ -3,9 +3,11 @@ #define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS #include +#include + +struct CommandBuffer; + struct Texture { - vk::Image image; - vk::DeviceMemory image_alloc; - Texture(vk::Device dev, vk::PhysicalDevice phys_dev, const std::string& fname); - void cleanup(vk::Device dev); + std::unique_ptr image; + Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer command_buffer, const std::string& fname); }; \ No newline at end of file diff --git a/pléascach.cpp b/pléascach.cpp index 85cfdd6..b874ca1 100644 --- a/pléascach.cpp +++ b/pléascach.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include int main(int argc, char* argv[]) { @@ -12,6 +14,7 @@ int main(int argc, char* argv[]) { Renderer ren(win); while (!in->shouldClose()) { + Timer frame_timer; in->poll(); while (in->queue.size()) { @@ -39,6 +42,7 @@ int main(int argc, char* argv[]) { ren.draw(); ren.present(); + Log::debug("Frame: %lf milliseconds (60fps ~ 16.67)\n", frame_timer.stop()); } } catch (const std::string& e) { diff --git a/util/Timer.hpp b/util/Timer.hpp new file mode 100644 index 0000000..3e548c8 --- /dev/null +++ b/util/Timer.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +/* get the time in milliseconds */ +struct Timer { + std::chrono::time_point start_time, end_time; + bool running = false; + + + Timer() { + start(); + } + + inline void start() { + start_time = std::chrono::steady_clock::now(); + running = true; + } + + inline void reset() { + start(); + } + + inline double read() { + if (running) { + auto end = std::chrono::steady_clock::now(); + return std::chrono::duration_cast(end - start_time).count() / 1000.0; + } + + return std::chrono::duration_cast(end_time - start_time).count() / 1000.0; + } + + inline double stop() { + end_time = std::chrono::steady_clock::now(); + running = false; + return read(); + } +}; \ No newline at end of file diff --git a/util/log.hpp b/util/log.hpp index 462c1e7..65d343d 100644 --- a/util/log.hpp +++ b/util/log.hpp @@ -12,7 +12,7 @@ namespace Log { return static_cast(static_cast(a) | static_cast(b)); } - static const MessageLevelBit log_mask = ERROR | INFO; + static const MessageLevelBit log_mask = ERROR | INFO | DEBUG; template static void print(MessageLevelBit level, const std::string& fmt, Args... args) {