Compare commits

...

2 Commits

Author SHA1 Message Date
Anish Bhobe 1a8f113323 Vertex buffers. 2024-07-08 16:06:09 +02:00
Anish Bhobe 7c17d09822 Cross compatibility with older linux SDK. 2024-07-08 16:06:09 +02:00
8 changed files with 273 additions and 34 deletions

View File

@ -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)

View File

@ -5,6 +5,8 @@
#include "buffer.h"
#include <ranges>
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<vk::Result>(vmaCreateBuffer(device->m_Allocator, Recast<VkBufferCreateInfo *>(&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<VkMemoryPropertyFlags *>(&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<vk::Result>(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<vk::Result>(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);
}

View File

@ -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);
};
};
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;
}

View File

@ -33,14 +33,12 @@ struct Device final
~Device();
template <typename T>
requires vk::isVulkanHandleType<T>::value
void SetName(const T &object, cstr name) const;
requires vk::isVulkanHandleType<T>::value void SetName(const T &object, cstr name) const;
[[nodiscard]] vk::Queue GetQueue(u32 familyIndex, u32 queueIndex) const;
};
template <typename T>
requires vk::isVulkanHandleType<T>::value
void
requires vk::isVulkanHandleType<T>::value void
Device::SetName(const T &object, cstr name) const
{
if (!name)

View File

@ -30,7 +30,7 @@
#include <vulkan/vulkan.hpp>
constexpr u32 ASTER_API_VERSION = vk::ApiVersion13;
constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
#define CODE_LOC " @ " __FILE__ ":" VULKAN_HPP_STRINGIFY(__LINE__)

View File

@ -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)

View File

@ -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); //*/
}

View File

@ -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 <EASTL/array.h>
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<vk::VertexInputAttributeDescription, 2>
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;
@ -51,16 +83,80 @@ main(int, char **)
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
Features enabledDeviceFeatures = {.m_Vulkan13Features = {.dynamicRendering = vk::True}};
Features enabledDeviceFeatures = {.m_Vulkan13Features = {.dynamicRendering = true}};
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, &copyPool);
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, &copyBuffer);
ERROR_IF(Failed(result), "Copy command buffer allocation failed. Cause: {}", result) THEN_ABORT(result);
}
// eastl::array<Vertex, 3> 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 = &copyBuffer,
};
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<u64>);
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,12 +201,13 @@ main(int, char **)
frames.emplace_back(&device, queueAllocation.m_Family, i);
}
INFO("Starting loop");
u32 frameIndex = 0;
while (window.Poll())
{
Frame *currentFrame = &frames[frameIndex];
auto result = device.m_Device.waitForFences(1, &currentFrame->m_FrameAvailableFence, vk::True, MaxValue<u64>);
auto result = device.m_Device.waitForFences(1, &currentFrame->m_FrameAvailableFence, true, MaxValue<u64>);
ERROR_IF(Failed(result), "Waiting for fence {} failed. Cause: {}", frameIndex, result)
THEN_ABORT(result);
@ -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,15 +417,18 @@ 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<u32>(inputAttributeDescription.size()),
.pVertexAttributeDescriptions = inputAttributeDescription.data(),
};
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = {
.topology = vk::PrimitiveTopology::eTriangleList,
.primitiveRestartEnable = vk::False,
.primitiveRestartEnable = false,
};
vk::PipelineViewportStateCreateInfo viewportStateCreateInfo = {
@ -331,24 +437,24 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
};
vk::PipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = {
.depthClampEnable = vk::False,
.rasterizerDiscardEnable = vk::False,
.depthClampEnable = false,
.rasterizerDiscardEnable = false,
.polygonMode = vk::PolygonMode::eFill,
.cullMode = vk::CullModeFlagBits::eNone,
.frontFace = vk::FrontFace::eCounterClockwise,
.depthBiasEnable = vk::False,
.depthBiasEnable = false,
.lineWidth = 1.0,
};
vk::PipelineMultisampleStateCreateInfo multisampleStateCreateInfo = {
.rasterizationSamples = vk::SampleCountFlagBits::e1,
.sampleShadingEnable = vk::False,
.sampleShadingEnable = false,
};
vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = {
.depthTestEnable = vk::False,
.depthWriteEnable = vk::False,
.depthTestEnable = false,
.depthWriteEnable = false,
};
vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = {
.blendEnable = vk::False,
.blendEnable = false,
.srcColorBlendFactor = vk::BlendFactor::eSrcColor,
.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcColor,
.colorBlendOp = vk::BlendOp::eAdd,
@ -359,7 +465,7 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
};
vk::PipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = {
.logicOpEnable = vk::False,
.logicOpEnable = false,
.attachmentCount = 1,
.pAttachments = &colorBlendAttachmentState,
};