From 1a8f113323133f5769a727091869d7ce542f7aaf Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Sun, 7 Jul 2024 18:46:38 +0200 Subject: [PATCH] Vertex buffers. --- CMakeLists.txt | 2 +- aster/buffer.cpp | 81 ++++++++++-- aster/buffer.h | 47 ++++++- aster/device.h | 6 +- samples/01_triangle/CMakeLists.txt | 2 +- samples/01_triangle/shader/triangle.vert.glsl | 27 ++++ samples/01_triangle/triangle.cpp | 118 +++++++++++++++++- 7 files changed, 261 insertions(+), 22 deletions(-) create mode 100644 samples/01_triangle/shader/triangle.vert.glsl diff --git a/CMakeLists.txt b/CMakeLists.txt index aa054ea..4f9378d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ if (MSVC) add_compile_definitions(_HAS_EXCEPTIONS=1) add_compile_definitions(_CRT_SECURE_NO_WARNINGS) else () - set(CMAKE_CXX_FLAGS "-Wall -fno-rtti -fno-exceptions") + set(CMAKE_CXX_FLAGS "-Wall -g -fno-rtti -fno-exceptions") endif () include(add_shader.cmake) diff --git a/aster/buffer.cpp b/aster/buffer.cpp index 2e12793..e820f18 100644 --- a/aster/buffer.cpp +++ b/aster/buffer.cpp @@ -5,6 +5,8 @@ #include "buffer.h" +#include + void Buffer::Destroy(const Device *device) { @@ -16,28 +18,91 @@ Buffer::Destroy(const Device *device) } void -UniformBuffer::Init(const Device *device, usize size, cstr name) +Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage, + VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name) { + assert(size <= SIZE_MASK); + vk::BufferCreateInfo bufferCreateInfo = { .size = size, - .usage = vk::BufferUsageFlagBits::eUniformBuffer, + .usage = bufferUsage, .sharingMode = vk::SharingMode::eExclusive, }; - constexpr VmaAllocationCreateInfo allocationCreateInfo = { - .flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | - VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, - .usage = VMA_MEMORY_USAGE_AUTO, + const VmaAllocationCreateInfo allocationCreateInfo = { + .flags = allocationFlags, + .usage = memoryUsage, }; VkBuffer buffer; VmaAllocation allocation; + VmaAllocationInfo allocationInfo; auto result = Cast(vmaCreateBuffer(device->m_Allocator, Recast(&bufferCreateInfo), - &allocationCreateInfo, &buffer, &allocation, nullptr)); + &allocationCreateInfo, &buffer, &allocation, &allocationInfo)); ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result); m_Buffer = buffer; - m_Size = size; + m_Size = size & SIZE_MASK; m_Allocation = allocation; + m_Mapped = allocationInfo.pMappedData; + + vk::MemoryPropertyFlags memoryPropertyFlags; + vmaGetAllocationMemoryProperties(device->m_Allocator, allocation, + Recast(&memoryPropertyFlags)); + if (memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible) + { + m_Size |= HOST_ACCESSIBLE_BIT; + } device->SetName(m_Buffer, name); +} + +void +Buffer::Write(const Device *device, usize offset, usize size, void *data) +{ + assert(IsHostVisible()); + + if (!IsMapped()) + { + auto result = Cast(vmaMapMemory(device->m_Allocator, m_Allocation, &m_Mapped)); + ERROR_IF(Failed(result), "Memory mapping failed. Cause: {}", result); + if (!Failed(result)) + { + memcpy(m_Mapped, data, size); + + vmaUnmapMemory(device->m_Allocator, m_Allocation); + m_Mapped = nullptr; + } + } + else + { + memcpy(m_Mapped, data, size); + } + + // TODO: Debug this. + // auto result = Cast(vmaCopyMemoryToAllocation(device->m_Allocator, &data, m_Allocation, 0, size)); + // ERROR_IF(Failed(result), "Writing to buffer failed. Cause: {}", result) THEN_ABORT(result); +} + +void +UniformBuffer::Init(const Device *device, const usize size, const cstr name) +{ + Allocate(device, size, vk::BufferUsageFlagBits::eUniformBuffer, + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, + VMA_MEMORY_USAGE_AUTO, name); +} + +void +VertexBuffer::Init(const Device *device, usize size, cstr name) +{ + Allocate(device, size, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst, + VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name); +} + +void +StagingBuffer::Init(const Device *device, usize size, cstr name) +{ + Allocate(device, size, vk::BufferUsageFlagBits::eTransferSrc, + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, + VMA_MEMORY_USAGE_AUTO, name); } \ No newline at end of file diff --git a/aster/buffer.h b/aster/buffer.h index 5317b79..477c576 100644 --- a/aster/buffer.h +++ b/aster/buffer.h @@ -14,12 +14,55 @@ struct Buffer { vk::Buffer m_Buffer = nullptr; VmaAllocation m_Allocation = nullptr; - usize m_Size = 0; + void *m_Mapped = nullptr; + + [[nodiscard]] usize GetSize() const; + [[nodiscard]] bool IsHostVisible() const; + [[nodiscard]] bool IsMapped() const; void Destroy(const Device *device); + void Write(const Device *device, usize offset, usize size, void *data); + + protected: + void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage, + VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name); + + usize m_Size = 0; + + constexpr static usize HOST_ACCESSIBLE_BIT = 1llu << 63; + constexpr static usize SIZE_MASK = ~HOST_ACCESSIBLE_BIT; }; struct UniformBuffer : Buffer { void Init(const Device *device, usize size, cstr name = nullptr); -}; \ No newline at end of file +}; + +struct VertexBuffer : Buffer +{ + void Init(const Device *device, usize size, cstr name = nullptr); + void Write(const Device *device, void *data, usize size, usize offset) const = delete; +}; + +struct StagingBuffer : Buffer +{ + void Init(const Device *device, usize size, cstr name = nullptr); +}; + +inline usize +Buffer::GetSize() const +{ + return m_Size & SIZE_MASK; +} + +inline bool +Buffer::IsHostVisible() const +{ + return m_Size & HOST_ACCESSIBLE_BIT; +} + +inline bool +Buffer::IsMapped() const +{ + return m_Mapped; +} diff --git a/aster/device.h b/aster/device.h index ba5a298..c104405 100644 --- a/aster/device.h +++ b/aster/device.h @@ -33,14 +33,12 @@ struct Device final ~Device(); template - requires vk::isVulkanHandleType::value - void SetName(const T &object, cstr name) const; + requires vk::isVulkanHandleType::value void SetName(const T &object, cstr name) const; [[nodiscard]] vk::Queue GetQueue(u32 familyIndex, u32 queueIndex) const; }; template - requires vk::isVulkanHandleType::value -void +requires vk::isVulkanHandleType::value void Device::SetName(const T &object, cstr name) const { if (!name) diff --git a/samples/01_triangle/CMakeLists.txt b/samples/01_triangle/CMakeLists.txt index 168266a..9aa4b20 100644 --- a/samples/01_triangle/CMakeLists.txt +++ b/samples/01_triangle/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.13) add_executable(triangle "triangle.cpp") -add_shader(triangle shader/triangle.vs.hlsl) +add_shader(triangle shader/triangle.vert.glsl) add_shader(triangle shader/triangle.frag.glsl) target_link_libraries(triangle PRIVATE aster_core) diff --git a/samples/01_triangle/shader/triangle.vert.glsl b/samples/01_triangle/shader/triangle.vert.glsl new file mode 100644 index 0000000..ea50725 --- /dev/null +++ b/samples/01_triangle/shader/triangle.vert.glsl @@ -0,0 +1,27 @@ +#version 450 +#pragma shader_stage(vertex) + +layout(location=0) in vec4 position; +layout(location=1) in vec4 color; + +layout(location=0) out vec3 outColor; + +void main() { + /* + vec3 points[] = { + vec3(-0.5f, -0.5f, 0.0f), + vec3(0.5f, -0.5f, 0.0f), + vec3(0.0f, 0.5f, 0.0f) + }; + vec3 colors[] = { + vec3( 1.0f, 0.0f, 0.0f ), + vec3( 0.0f, 1.0f, 0.0f ), + vec3( 0.0f, 0.0f, 1.0f ), + }; + + gl_Position = vec4(points[gl_VertexIndex], 1.0f); + outColor = vec3(colors[gl_VertexIndex]); //*/ + //* + gl_Position = vec4(position.xyz, 1.0f); + outColor = vec3(color.rgb); //*/ +} \ No newline at end of file diff --git a/samples/01_triangle/triangle.cpp b/samples/01_triangle/triangle.cpp index 1024067..e0d348c 100644 --- a/samples/01_triangle/triangle.cpp +++ b/samples/01_triangle/triangle.cpp @@ -3,6 +3,7 @@ // Copyright (c) 2020-2024 Anish Bhobe // ============================================= +#include "buffer.h" #include "constants.h" #include "context.h" #include "device.h" @@ -18,12 +19,43 @@ #include constexpr u32 MAX_FRAMES_IN_FLIGHT = 3; -constexpr auto VERTEX_SHADER_FILE = "shader/triangle.vs.hlsl.spv"; +constexpr auto VERTEX_SHADER_FILE = "shader/triangle.vert.glsl.spv"; constexpr auto FRAGMENT_SHADER_FILE = "shader/triangle.frag.glsl.spv"; vk::ShaderModule CreateShader(const Device *device, cstr shaderFile); Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain); +struct Vertex +{ + float m_Position[4]; + float m_Color[4]; + + constexpr static vk::VertexInputBindingDescription + GetBinding(const u32 binding) + { + return {.binding = binding, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex}; + } + + constexpr static eastl::array + GetAttributes(const u32 binding) + { + return { + vk::VertexInputAttributeDescription{ + .location = 0, + .binding = binding, + .format = vk::Format::eR32G32B32A32Sfloat, + .offset = offsetof(Vertex, m_Position), + }, + vk::VertexInputAttributeDescription{ + .location = 1, + .binding = binding, + .format = vk::Format::eR32G32B32A32Sfloat, + .offset = offsetof(Vertex, m_Color), + }, + }; + } +}; + struct Frame { const Device *m_Device; @@ -55,12 +87,76 @@ main(int, char **) QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse); Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"}; - vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 1); + vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0); Swapchain swapchain = {&window, &device, "Primary Chain"}; Pipeline pipeline = CreatePipeline(&device, &swapchain); + vk::CommandPool copyPool; + vk::CommandBuffer copyBuffer; + { + vk::CommandPoolCreateInfo poolCreateInfo = { + .flags = vk::CommandPoolCreateFlagBits::eTransient, + .queueFamilyIndex = queueAllocation.m_Family, + }; + auto result = device.m_Device.createCommandPool(&poolCreateInfo, nullptr, ©Pool); + ERROR_IF(Failed(result), "Copy command pool creation failed. Cause: {}", result) THEN_ABORT(result); + vk::CommandBufferAllocateInfo bufferAllocateInfo = { + .commandPool = copyPool, + .level = vk::CommandBufferLevel::ePrimary, + .commandBufferCount = 1, + }; + result = device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, ©Buffer); + ERROR_IF(Failed(result), "Copy command buffer allocation failed. Cause: {}", result) THEN_ABORT(result); + } + + // eastl::array vertices{}; + eastl::array vertices = { + Vertex{.m_Position = {-0.5f, -0.5f, 0.0f, 1.0f}, .m_Color = {1.0f, 0.0f, 0.0f, 1.0f}}, + Vertex{.m_Position = {0.5f, -0.5f, 0.0f, 1.0f}, .m_Color = {0.0f, 1.0f, 0.0f, 1.0f}}, + Vertex{.m_Position = {0.0f, 0.5f, 0.0f, 1.0f}, .m_Color = {0.0f, 0.0f, 1.0f, 1.0f}}, + }; + VertexBuffer vbo; + vbo.Init(&device, vertices.size() * sizeof vertices[0], "VBO"); + { + StagingBuffer staging; + staging.Init(&device, vertices.size() * sizeof vertices[0], "Staging"); + staging.Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data()); + + vk::Fence fence; + vk::FenceCreateInfo fenceCreateInfo = {}; + auto result = device.m_Device.createFence(&fenceCreateInfo, nullptr, &fence); + ERROR_IF(Failed(result), "Fence creation failed. Cause: {}", result) THEN_ABORT(result); + + vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; + result = copyBuffer.begin(&beginInfo); + ERROR_IF(Failed(result), "Copy begin failed. Cause: {}", result) THEN_ABORT(result); + + vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = staging.GetSize()}; + copyBuffer.copyBuffer(staging.m_Buffer, vbo.m_Buffer, 1, &bufferCopy); + + result = copyBuffer.end(); + ERROR_IF(Failed(result), "Copy end failed. Cause: {}", result) THEN_ABORT(result); + + vk::SubmitInfo submitInfo = { + .commandBufferCount = 1, + .pCommandBuffers = ©Buffer, + }; + + result = commandQueue.submit(1, &submitInfo, fence); + ERROR_IF(Failed(result), "Submit failed. Cause: {}", result) THEN_ABORT(result) ELSE_INFO("Submit copy"); + + result = device.m_Device.waitForFences(1, &fence, true, MaxValue); + ERROR_IF(Failed(result), "Fence wait failed. Cause: {}", result) THEN_ABORT(result) ELSE_INFO("Fence wait"); + + result = device.m_Device.resetCommandPool(copyPool, {}); + ERROR_IF(Failed(result), "Couldn't reset command pool. Cause: {}", result) THEN_ABORT(result); + + device.m_Device.destroy(fence, nullptr); + staging.Destroy(&device); + } + // Persistent variables vk::Viewport viewport = { .x = 0, @@ -105,6 +201,7 @@ main(int, char **) frames.emplace_back(&device, queueAllocation.m_Family, i); } + INFO("Starting loop"); u32 frameIndex = 0; while (window.Poll()) { @@ -181,6 +278,8 @@ main(int, char **) cmd.setViewport(0, 1, &viewport); cmd.setScissor(0, 1, &scissor); cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); + usize offsets = 0; + cmd.bindVertexBuffers(0, 1, &vbo.m_Buffer, &offsets); cmd.draw(3, 1, 0, 0); cmd.endRendering(); @@ -239,6 +338,9 @@ main(int, char **) auto result = device.m_Device.waitIdle(); ERROR_IF(Failed(result), "Wait idle failed. Cause: {}", result); + device.m_Device.destroy(copyPool, nullptr); + vbo.Destroy(&device); + return 0; } @@ -283,6 +385,7 @@ Frame::AllocateCommandBuffer() const return commandBuffer; } + Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain) { @@ -314,11 +417,14 @@ CreatePipeline(const Device *device, const Swapchain *swapchain) ERROR_IF(Failed(result), "Could not create a pipeline layout. Cause: {}", result) THEN_ABORT(result); device->SetName(pipelineLayout, "Triangle Layout"); + vk::VertexInputBindingDescription inputBindingDescription = Vertex::GetBinding(0); + auto inputAttributeDescription = Vertex::GetAttributes(0); + vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = { - .vertexBindingDescriptionCount = 0, - .pVertexBindingDescriptions = nullptr, - .vertexAttributeDescriptionCount = 0, - .pVertexAttributeDescriptions = nullptr, + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &inputBindingDescription, + .vertexAttributeDescriptionCount = Cast(inputAttributeDescription.size()), + .pVertexAttributeDescriptions = inputAttributeDescription.data(), }; vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = { .topology = vk::PrimitiveTopology::eTriangleList,