From b6a7634cc565abbfa7befe2bdd04e021f057afc0 Mon Sep 17 00:00:00 2001 From: bailwillharr Date: Sat, 22 Oct 2022 13:15:25 +0100 Subject: [PATCH] Get vertex buffers working --- include/gfx.hpp | 29 +++++++++--- include/gfx_device.hpp | 36 ++++----------- src/engine.cpp | 47 +++++++++++++++++--- src/gfx_device_opengl45.cpp | 2 +- src/gfx_device_vulkan.cpp | 89 ++++++++++++++++++++++++++++++++----- 5 files changed, 151 insertions(+), 52 deletions(-) diff --git a/include/gfx.hpp b/include/gfx.hpp index b5f2f83..c275dc1 100644 --- a/include/gfx.hpp +++ b/include/gfx.hpp @@ -2,8 +2,9 @@ #include #include +#include -namespace engine { +namespace engine::gfx { enum class ShaderType { VERTEX, @@ -23,10 +24,28 @@ namespace engine { TRIANGLE_STRIP, }; - enum class IndexBufferFormat { - UNSIGNED_8_BITS, - UNSIGNED_16_BITS, - UNSIGNED_32_BITS, + enum class VertexAttribFormat { + VEC2, + VEC3, }; + struct VertexBufferDesc { + uint64_t size; + }; + + struct VertexAttribDescription { + uint32_t location; + VertexAttribFormat format; + uint32_t offset; + }; + + struct VertexFormat { + uint32_t stride; + std::vector attributeDescriptions; + }; + + // handles (incomplete types) + + struct VertexBuffer; + } diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index f43c550..a3646da 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -2,36 +2,13 @@ #include "engine_api.h" +#include "gfx.hpp" + #include struct SDL_Window; namespace engine { - - namespace gfx { - - enum class BufferUsage { - DEFAULT, - UPLOAD, - READBACK, - }; - - enum class BindFlag { - NONE = 0, - UNIFORM_BUFFER = 1 << 0, - }; - - struct BufferDesc { - uint64_t size; - BufferUsage usage; - BindFlag bindFlags; - }; - - // handles (incomplete types) - - class BufferHandle; - - }; class ENGINE_API GFXDevice { @@ -42,15 +19,18 @@ namespace engine { GFXDevice& operator=(const GFXDevice&) = delete; ~GFXDevice(); + // adds a vertex buffer draw call to the queue + void drawBuffer(const gfx::VertexBuffer* vb); + // Call once per frame. Executes all queued draw calls and renders to the screen. void draw(); // creates the equivalent of an OpenGL shader program & vertex attrib configuration - void createPipeline(const char* vertShaderPath, const char* fragShaderPath); + void createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat); // creates a vertex array for holding mesh data - gfx::BufferHandle* createVertexBuffer(const gfx::BufferDesc& desc, const void* vertices, const void* indices); - void destroyBuffer(const gfx::BufferHandle* buffer); + gfx::VertexBuffer* createVertexBuffer(uint32_t size, const void* vertices, const void* indices); + void destroyVertexBuffer(const gfx::VertexBuffer* buffer); // wait until all the active GPU queues have finished working void waitIdle(); diff --git a/src/engine.cpp b/src/engine.cpp index 5d69831..ad31eb8 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -5,6 +5,11 @@ #include "resource_manager.hpp" #include "log.hpp" +#include + +static engine::gfx::VertexBuffer* buffer; +static engine::gfx::VertexBuffer* buffer2; + namespace engine { Application::Application(const char* appName, const char* appVersion) @@ -14,22 +19,48 @@ namespace engine { engine::ResourceManager resMan{}; - m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str()); - - gfx::BufferDesc bufferDesc { - .size = 65536, + struct Vertex { + glm::vec2 pos; + glm::vec3 col; }; - gfx::BufferHandle* buffer = m_gfx->createBuffer(bufferDesc, nullptr); - m_gfx->destroyBuffer(buffer); + gfx::VertexFormat vertFormat{ + .stride = (uint32_t)sizeof(Vertex), + }; + + vertFormat.attributeDescriptions.push_back({0, gfx::VertexAttribFormat::VEC2, 0}); + vertFormat.attributeDescriptions.push_back({1, gfx::VertexAttribFormat::VEC3, offsetof(Vertex, col)}); + + m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str(), vertFormat); + + + + const std::vector vertices = { + { { 0.0f, -0.5f}, {1.0f, 0.0f, 0.0f} }, + { { 0.5f, 0.5f}, {0.0f, 1.0f, 0.0f} }, + { {-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f} } + }; + buffer = m_gfx->createVertexBuffer(sizeof(Vertex) * vertices.size(), vertices.data(), nullptr); + + const std::vector vertices2 = { + { { 0.9f, -0.9f}, {1.0f, 0.0f, 0.0f} }, + { { 0.9f, -0.8f}, {1.0f, 0.0f, 0.0f} }, + { { 0.8f, -0.9f}, {1.0f, 0.0f, 0.0f} } + }; + buffer2 = m_gfx->createVertexBuffer(sizeof(Vertex) * vertices2.size(), vertices2.data(), nullptr); + } Application::~Application() { + m_gfx->destroyVertexBuffer(buffer); + m_gfx->destroyVertexBuffer(buffer2); } void Application::gameLoop() { + TRACE("Begin game loop..."); + uint64_t lastTick = m_win->getNanos(); constexpr int TICKFREQ = 1; // in hz @@ -54,6 +85,10 @@ namespace engine { } /* draw */ + + m_gfx->drawBuffer(buffer); + m_gfx->drawBuffer(buffer2); + m_gfx->draw(); /* poll events */ diff --git a/src/gfx_device_opengl45.cpp b/src/gfx_device_opengl45.cpp index 9b42ec9..929ff25 100644 --- a/src/gfx_device_opengl45.cpp +++ b/src/gfx_device_opengl45.cpp @@ -1,4 +1,4 @@ -// The implementation of the graphics layer using Vulkan 1.3. +// The implementation of the graphics layer using OpenGL 4.5 // This uses SDL specific code #ifdef ENGINE_BUILD_OPENGL diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 0379543..447f101 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace engine { @@ -77,13 +78,29 @@ namespace engine { // handles - struct gfx::BufferHandle { + struct gfx::VertexBuffer { VkBuffer buffer = VK_NULL_HANDLE; VmaAllocation allocation = nullptr; + uint32_t size; }; + // enum converters + + namespace vkinternal { + + static VkFormat getVertexAttribFormat(gfx::VertexAttribFormat fmt) + { + switch (fmt) { + case gfx::VertexAttribFormat::VEC2: + return VK_FORMAT_R32G32_SFLOAT; + case gfx::VertexAttribFormat::VEC3: + return VK_FORMAT_R32G32B32_SFLOAT; + } + } + } + // functions static std::vector readFile(const std::string& filename) @@ -532,6 +549,8 @@ namespace engine { Pipeline pipeline{}; + std::queue drawQueue{}; + }; GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) @@ -952,6 +971,11 @@ namespace engine { vkDestroyInstance(pimpl->instance, nullptr); } + void GFXDevice::drawBuffer(const gfx::VertexBuffer* vb) + { + pimpl->drawQueue.push(vb); + } + void GFXDevice::draw() { VkResult res; @@ -995,7 +1019,6 @@ namespace engine { renderPassInfo.pClearValues = &clearColor; vkCmdBeginRenderPass(pimpl->commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(pimpl->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pimpl->pipeline.handle); VkViewport viewport{}; viewport.x = 0.0f; @@ -1011,7 +1034,20 @@ namespace engine { scissor.extent = pimpl->swapchain.extent; vkCmdSetScissor(pimpl->commandBuffer, 0, 1, &scissor); - vkCmdDraw(pimpl->commandBuffer, 3, 1, 0, 0); + // run queued draw calls + + vkCmdBindPipeline(pimpl->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pimpl->pipeline.handle); + VkDeviceSize offsets[] = { 0 }; + while (pimpl->drawQueue.empty() == false) { + + const auto* buffer = pimpl->drawQueue.front(); + + vkCmdBindVertexBuffers(pimpl->commandBuffer, 0, 1, &buffer->buffer, offsets); + vkCmdDraw(pimpl->commandBuffer, buffer->size, 1, 0, 0); + + pimpl->drawQueue.pop(); + + } vkCmdEndRenderPass(pimpl->commandBuffer); @@ -1054,11 +1090,28 @@ namespace engine { } } - void GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath) + void GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat) { VkResult res; + // get vertex attrib layout: + VkVertexInputBindingDescription bindingDescription{ }; + bindingDescription.binding = 0; + bindingDescription.stride = vertexFormat.stride; + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + std::vector attribDescs{}; + attribDescs.reserve(vertexFormat.attributeDescriptions.size()); + for (const auto& desc : vertexFormat.attributeDescriptions) { + VkVertexInputAttributeDescription vulkanAttribDesc{}; + vulkanAttribDesc.binding = 0; + vulkanAttribDesc.location = desc.location; + vulkanAttribDesc.offset = desc.offset; + vulkanAttribDesc.format = vkinternal::getVertexAttribFormat(desc.format); + attribDescs.push_back(vulkanAttribDesc); + } + auto vertShaderCode = readFile(vertShaderPath); auto fragShaderCode = readFile(fragShaderPath); INFO("Opened shader: {}", std::filesystem::path(vertShaderPath).filename().string()); @@ -1080,12 +1133,13 @@ namespace engine { VkPipelineShaderStageCreateInfo shaderStages[2] = { vertShaderStageInfo, fragShaderStageInfo }; + // this sets "vertex attribute pointers" VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = 0; - vertexInputInfo.pVertexBindingDescriptions = nullptr; - vertexInputInfo.vertexAttributeDescriptionCount = 0; - vertexInputInfo.pVertexAttributeDescriptions = nullptr; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = attribDescs.size(); + vertexInputInfo.pVertexAttributeDescriptions = attribDescs.data(); VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; @@ -1205,24 +1259,35 @@ namespace engine { } - gfx::BufferHandle* GFXDevice::createBuffer(const gfx::BufferDesc& desc, const void* data) + gfx::VertexBuffer* GFXDevice::createVertexBuffer(uint32_t size, const void* vertices, const void* indices) { - auto out = new gfx::BufferHandle{}; + auto out = new gfx::VertexBuffer{}; + out->size = size; VkBufferCreateInfo createInfo{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; - createInfo.size = desc.size; + createInfo.size = out->size; createInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.flags = 0; VmaAllocationCreateInfo allocInfo{}; allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; VkResult res = vmaCreateBuffer(pimpl->allocator, &createInfo, &allocInfo, &out->buffer, &out->allocation, nullptr); assert(res == VK_SUCCESS); + void* data; + res = vmaMapMemory(pimpl->allocator, out->allocation, &data); + assert(res == VK_SUCCESS); + memcpy(data, vertices, out->size); + vmaUnmapMemory(pimpl->allocator, out->allocation); + return out; } - void GFXDevice::destroyBuffer(const gfx::BufferHandle* buffer) + void GFXDevice::destroyVertexBuffer(const gfx::VertexBuffer* buffer) { vmaDestroyBuffer(pimpl->allocator, buffer->buffer, buffer->allocation); delete buffer;