Added class to handle allocation and creation of Buffers and Image, as well as Timers.

No memory leaks detected yet.
(Fingers crossed).
This commit is contained in:
connellpaxton 2024-01-23 16:00:15 -05:00
parent 952176f4c9
commit e077eeaf5d
15 changed files with 226 additions and 95 deletions

30
Memory/Buffer.cpp Normal file
View File

@ -0,0 +1,30 @@
#include <Memory/Buffer.hpp>
#include <Memory/Memory.hpp>
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);
}

27
Memory/Buffer.hpp Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
#include <vulkan/vulkan.hpp>
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<uint8_t>& data) {
map(data.data(), size);
}
operator vk::Buffer& () {
return buffer;
}
~Buffer();
};

44
Memory/Image.cpp Normal file
View File

@ -0,0 +1,44 @@
#include <Memory/Image.hpp>
#include <Memory/Memory.hpp>
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);
}

20
Memory/Image.hpp Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
#include <vulkan/vulkan.hpp>
#include <util/log.hpp>
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();
};

View File

@ -1,6 +1,8 @@
#include <Renderer/CommandBuffer.hpp> #include <Renderer/CommandBuffer.hpp>
#include <Renderer/Pipeline.hpp> #include <Renderer/Pipeline.hpp>
#include <Memory/Buffer.hpp>
#include <Memory/Image.hpp>
#include <util/log.hpp> #include <util/log.hpp>
@ -32,8 +34,27 @@ void CommandBuffer::begin() {
command_buffer.begin(begin_info); command_buffer.begin(begin_info);
} }
void CommandBuffer::copy(vk::Buffer in, vk::Buffer out, vk::ArrayProxy<const vk::BufferCopy> regions) { void CommandBuffer::copy(vk::Buffer src, vk::Buffer dst, vk::ArrayProxy<const vk::BufferCopy> regions) {
command_buffer.copyBuffer(in, out, 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) { void CommandBuffer::bind(const GraphicsPipeline& pipeline) {

View File

@ -5,6 +5,9 @@
#include <util/int.hpp> #include <util/int.hpp>
struct Buffer;
struct Image;
struct GraphicsPipeline; struct GraphicsPipeline;
struct ComputePipeline; struct ComputePipeline;
@ -18,6 +21,8 @@ struct CommandBuffer {
/* copy between buffer */ /* copy between buffer */
void copy(vk::Buffer in, vk::Buffer out, vk::ArrayProxy<const vk::BufferCopy> regions); void copy(vk::Buffer in, vk::Buffer out, vk::ArrayProxy<const vk::BufferCopy> 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 GraphicsPipeline& pipeline);
void bind(const ComputePipeline& pipeline); void bind(const ComputePipeline& pipeline);

View File

@ -1,19 +0,0 @@
#include <Renderer/Pipeline.hpp>
#include <Renderer/Shader.hpp>
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,
};
}

View File

@ -5,15 +5,16 @@
#include <Memory/Memory.hpp> #include <Memory/Memory.hpp>
#include <util/Timer.hpp>
using namespace std::string_literals; using namespace std::string_literals;
Renderer::Renderer(Window& win) : win(win) { Renderer::Renderer(Window& win) : win(win) {
/* Create Instance object */ /* Create Instance object */
auto app_info = vk::ApplicationInfo { auto app_info = vk::ApplicationInfo {
.pApplicationName = "Pl<EFBFBD>ascach Demo", .pApplicationName = "Pléascach Demo",
.applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), .applicationVersion = VK_MAKE_API_VERSION(0, 0, 1, 0),
.pEngineName = "Pl<EFBFBD>ascach", .pEngineName = "Pléascach",
.engineVersion = VK_MAKE_API_VERSION(0, 0, 1, 0), .engineVersion = VK_MAKE_API_VERSION(0, 0, 1, 0),
.apiVersion = VK_API_VERSION_1_1, .apiVersion = VK_API_VERSION_1_1,
}; };
@ -162,7 +163,6 @@ Renderer::Renderer(Window& win) : win(win) {
} }
void Renderer::draw() { void Renderer::draw() {
Log::debug("draw() called \n");
if(dev.waitForFences(render_fence, true, UINT64_MAX) != vk::Result::eSuccess) { if(dev.waitForFences(render_fence, true, UINT64_MAX) != vk::Result::eSuccess) {
Log::error("Failed to wait for fences in draw()\n"); Log::error("Failed to wait for fences in draw()\n");
@ -240,7 +240,6 @@ void Renderer::draw() {
} }
void Renderer::present() { void Renderer::present() {
Log::debug("present() called \n");
auto present_info = vk::PresentInfoKHR{ auto present_info = vk::PresentInfoKHR{
.waitSemaphoreCount = 1, .waitSemaphoreCount = 1,
.pWaitSemaphores = &render_wait_semaphore, .pWaitSemaphores = &render_wait_semaphore,

View File

@ -2,6 +2,7 @@
#include <Window/Window.hpp> #include <Window/Window.hpp>
#include <Memory/Memory.hpp> #include <Memory/Memory.hpp>
#include <Memory/Image.hpp>
#include <util/log.hpp> #include <util/log.hpp>
@ -66,38 +67,11 @@ void Swapchain::create(vk::SwapchainKHR old_swapchain) {
images = dev.getSwapchainImagesKHR(swapchain); images = dev.getSwapchainImagesKHR(swapchain);
auto depth_image_info = vk::ImageCreateInfo{ depth_image = std::make_unique<Image>(phys_dev, dev, vk::Extent3D { extent.width, extent.height, 1 }, vk::Format::eD16Unorm,
.imageType = vk::ImageType::e2D, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal);
.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 = 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 { auto depth_view_info = vk::ImageViewCreateInfo {
.image = depth_image, .image = depth_image->image,
.viewType = vk::ImageViewType::e2D, .viewType = vk::ImageViewType::e2D,
.format = vk::Format::eD16Unorm, .format = vk::Format::eD16Unorm,
.components = { .components = {
@ -152,7 +126,6 @@ void Swapchain::create(vk::SwapchainKHR old_swapchain) {
framebuffers[i] = dev.createFramebuffer(framebuffer_info); framebuffers[i] = dev.createFramebuffer(framebuffer_info);
} }
} }
@ -172,8 +145,7 @@ void Swapchain::cleanup() {
dev.destroyImageView(view); dev.destroyImageView(view);
dev.destroyImageView(depth_image_view); dev.destroyImageView(depth_image_view);
dev.destroyImage(depth_image); depth_image->cleanup();
dev.freeMemory(depth_alloc);
} }
Swapchain::~Swapchain() { Swapchain::~Swapchain() {

View File

@ -7,6 +7,7 @@
#include <Renderer/RenderPass.hpp> #include <Renderer/RenderPass.hpp>
struct Window; struct Window;
struct Image;
struct Swapchain { struct Swapchain {
Swapchain(Window& win, vk::Device dev, vk::PhysicalDevice phys_dev, const vk::SurfaceKHR& surface, RenderPass render_pass); 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<vk::ImageView> views; std::vector<vk::ImageView> views;
std::vector<vk::Framebuffer> framebuffers; std::vector<vk::Framebuffer> framebuffers;
vk::Image depth_image; std::unique_ptr<Image> depth_image;
vk::ImageView depth_image_view; vk::ImageView depth_image_view;
vk::DeviceMemory depth_alloc;
vk::Format format; vk::Format format;

View File

@ -1,46 +1,34 @@
#include <Resources/Texture.hpp> #include <Resources/Texture.hpp>
#include <Memory/Buffer.hpp>
#include <Memory/Memory.hpp> #include <Memory/Memory.hpp>
#include <Renderer/CommandBuffer.hpp>
#include <util/file.hpp> #include <util/file.hpp>
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h> #include <stb_image.h>
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; vk::Extent3D extent;
auto image_data = stbi_load(fname.c_str(), reinterpret_cast<int*>(&extent.width), reinterpret_cast<int*>(&extent.height), NULL, NULL);
auto image_data = stbi_load(fname.c_str(), reinterpret_cast<int*>(&extent.width), reinterpret_cast<int*>(&extent.height), &n_channels, STBI_rgb_alpha);
extent.depth = 1; extent.depth = 1;
auto image_info = vk::ImageCreateInfo{ image = std::make_unique<Image>(phys_dev, dev, extent, vk::Format::eR8G8B8A8Srgb,
.imageType = vk::ImageType::e2D, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eSampled,
.format = vk::Format::eR8G8B8A8Unorm, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
.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 = 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{ command_buffer.copy(staging, *image);
.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);
} }

View File

@ -3,9 +3,11 @@
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS #define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
#include <vulkan/vulkan.hpp> #include <vulkan/vulkan.hpp>
#include <Memory/Image.hpp>
struct CommandBuffer;
struct Texture { struct Texture {
vk::Image image; std::unique_ptr<Image> image;
vk::DeviceMemory image_alloc; Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer command_buffer, const std::string& fname);
Texture(vk::Device dev, vk::PhysicalDevice phys_dev, const std::string& fname);
void cleanup(vk::Device dev);
}; };

View File

@ -3,6 +3,8 @@
#include <Renderer/Renderer.hpp> #include <Renderer/Renderer.hpp>
#include <util/log.hpp> #include <util/log.hpp>
#include <util/Timer.hpp>
#include <iostream> #include <iostream>
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
@ -12,6 +14,7 @@ int main(int argc, char* argv[]) {
Renderer ren(win); Renderer ren(win);
while (!in->shouldClose()) { while (!in->shouldClose()) {
Timer frame_timer;
in->poll(); in->poll();
while (in->queue.size()) { while (in->queue.size()) {
@ -39,6 +42,7 @@ int main(int argc, char* argv[]) {
ren.draw(); ren.draw();
ren.present(); ren.present();
Log::debug("Frame: %lf milliseconds (60fps ~ 16.67)\n", frame_timer.stop());
} }
} catch (const std::string& e) { } catch (const std::string& e) {

38
util/Timer.hpp Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include <chrono>
/* get the time in milliseconds */
struct Timer {
std::chrono::time_point<std::chrono::steady_clock> 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<std::chrono::microseconds>(end - start_time).count() / 1000.0;
}
return std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count() / 1000.0;
}
inline double stop() {
end_time = std::chrono::steady_clock::now();
running = false;
return read();
}
};

View File

@ -12,7 +12,7 @@ namespace Log {
return static_cast<MessageLevelBit>(static_cast<uint32_t>(a) | static_cast<uint32_t>(b)); return static_cast<MessageLevelBit>(static_cast<uint32_t>(a) | static_cast<uint32_t>(b));
} }
static const MessageLevelBit log_mask = ERROR | INFO; static const MessageLevelBit log_mask = ERROR | INFO | DEBUG;
template<typename ...Args> template<typename ...Args>
static void print(MessageLevelBit level, const std::string& fmt, Args... args) { static void print(MessageLevelBit level, const std::string& fmt, Args... args) {