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

View File

@ -5,6 +5,9 @@
#include <util/int.hpp>
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<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 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 <util/Timer.hpp>
using namespace std::string_literals;
Renderer::Renderer(Window& win) : win(win) {
/* Create Instance object */
auto app_info = vk::ApplicationInfo {
.pApplicationName = "Pl<EFBFBD>ascach Demo",
.pApplicationName = "Pléascach Demo",
.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),
.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,

View File

@ -2,6 +2,7 @@
#include <Window/Window.hpp>
#include <Memory/Memory.hpp>
#include <Memory/Image.hpp>
#include <util/log.hpp>
@ -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 = 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);
depth_image = std::make_unique<Image>(phys_dev, dev, vk::Extent3D { extent.width, extent.height, 1 }, vk::Format::eD16Unorm,
vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal);
auto depth_view_info = vk::ImageViewCreateInfo {
.image = depth_image,
.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() {

View File

@ -7,6 +7,7 @@
#include <Renderer/RenderPass.hpp>
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<vk::ImageView> views;
std::vector<vk::Framebuffer> framebuffers;
vk::Image depth_image;
std::unique_ptr<Image> depth_image;
vk::ImageView depth_image_view;
vk::DeviceMemory depth_alloc;
vk::Format format;

View File

@ -1,46 +1,34 @@
#include <Resources/Texture.hpp>
#include <Memory/Buffer.hpp>
#include <Memory/Memory.hpp>
#include <Renderer/CommandBuffer.hpp>
#include <util/file.hpp>
#define STB_IMAGE_IMPLEMENTATION
#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;
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;
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<Image>(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);
}

View File

@ -3,9 +3,11 @@
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
#include <vulkan/vulkan.hpp>
#include <Memory/Image.hpp>
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> image;
Texture(vk::PhysicalDevice phys_dev, vk::Device dev, CommandBuffer command_buffer, const std::string& fname);
};

View File

@ -3,6 +3,8 @@
#include <Renderer/Renderer.hpp>
#include <util/log.hpp>
#include <util/Timer.hpp>
#include <iostream>
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) {

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));
}
static const MessageLevelBit log_mask = ERROR | INFO;
static const MessageLevelBit log_mask = ERROR | INFO | DEBUG;
template<typename ...Args>
static void print(MessageLevelBit level, const std::string& fmt, Args... args) {