Get vertex buffers working

This commit is contained in:
Bailey Harrison 2022-10-22 13:15:25 +01:00
parent eb1884ee8e
commit b6a7634cc5
5 changed files with 151 additions and 52 deletions

View File

@ -2,8 +2,9 @@
#include <cstdint> #include <cstdint>
#include <cstddef> #include <cstddef>
#include <vector>
namespace engine { namespace engine::gfx {
enum class ShaderType { enum class ShaderType {
VERTEX, VERTEX,
@ -23,10 +24,28 @@ namespace engine {
TRIANGLE_STRIP, TRIANGLE_STRIP,
}; };
enum class IndexBufferFormat { enum class VertexAttribFormat {
UNSIGNED_8_BITS, VEC2,
UNSIGNED_16_BITS, VEC3,
UNSIGNED_32_BITS,
}; };
struct VertexBufferDesc {
uint64_t size;
};
struct VertexAttribDescription {
uint32_t location;
VertexAttribFormat format;
uint32_t offset;
};
struct VertexFormat {
uint32_t stride;
std::vector<VertexAttribDescription> attributeDescriptions;
};
// handles (incomplete types)
struct VertexBuffer;
} }

View File

@ -2,36 +2,13 @@
#include "engine_api.h" #include "engine_api.h"
#include "gfx.hpp"
#include <memory> #include <memory>
struct SDL_Window; struct SDL_Window;
namespace engine { 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 { class ENGINE_API GFXDevice {
@ -42,15 +19,18 @@ namespace engine {
GFXDevice& operator=(const GFXDevice&) = delete; GFXDevice& operator=(const GFXDevice&) = delete;
~GFXDevice(); ~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. // Call once per frame. Executes all queued draw calls and renders to the screen.
void draw(); void draw();
// creates the equivalent of an OpenGL shader program & vertex attrib configuration // 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 // creates a vertex array for holding mesh data
gfx::BufferHandle* createVertexBuffer(const gfx::BufferDesc& desc, const void* vertices, const void* indices); gfx::VertexBuffer* createVertexBuffer(uint32_t size, const void* vertices, const void* indices);
void destroyBuffer(const gfx::BufferHandle* buffer); void destroyVertexBuffer(const gfx::VertexBuffer* buffer);
// wait until all the active GPU queues have finished working // wait until all the active GPU queues have finished working
void waitIdle(); void waitIdle();

View File

@ -5,6 +5,11 @@
#include "resource_manager.hpp" #include "resource_manager.hpp"
#include "log.hpp" #include "log.hpp"
#include <glm/glm.hpp>
static engine::gfx::VertexBuffer* buffer;
static engine::gfx::VertexBuffer* buffer2;
namespace engine { namespace engine {
Application::Application(const char* appName, const char* appVersion) Application::Application(const char* appName, const char* appVersion)
@ -14,22 +19,48 @@ namespace engine {
engine::ResourceManager resMan{}; engine::ResourceManager resMan{};
m_gfx->createPipeline(resMan.getFilePath("shader.vert.spv").string().c_str(), resMan.getFilePath("shader.frag.spv").string().c_str()); struct Vertex {
glm::vec2 pos;
gfx::BufferDesc bufferDesc { glm::vec3 col;
.size = 65536,
}; };
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<Vertex> 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<Vertex> 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() Application::~Application()
{ {
m_gfx->destroyVertexBuffer(buffer);
m_gfx->destroyVertexBuffer(buffer2);
} }
void Application::gameLoop() void Application::gameLoop()
{ {
TRACE("Begin game loop...");
uint64_t lastTick = m_win->getNanos(); uint64_t lastTick = m_win->getNanos();
constexpr int TICKFREQ = 1; // in hz constexpr int TICKFREQ = 1; // in hz
@ -54,6 +85,10 @@ namespace engine {
} }
/* draw */ /* draw */
m_gfx->drawBuffer(buffer);
m_gfx->drawBuffer(buffer2);
m_gfx->draw(); m_gfx->draw();
/* poll events */ /* poll events */

View File

@ -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 // This uses SDL specific code
#ifdef ENGINE_BUILD_OPENGL #ifdef ENGINE_BUILD_OPENGL

View File

@ -25,6 +25,7 @@
#include <fstream> #include <fstream>
#include <filesystem> #include <filesystem>
#include <optional> #include <optional>
#include <queue>
namespace engine { namespace engine {
@ -77,13 +78,29 @@ namespace engine {
// handles // handles
struct gfx::BufferHandle { struct gfx::VertexBuffer {
VkBuffer buffer = VK_NULL_HANDLE; VkBuffer buffer = VK_NULL_HANDLE;
VmaAllocation allocation = nullptr; 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 // functions
static std::vector<char> readFile(const std::string& filename) static std::vector<char> readFile(const std::string& filename)
@ -532,6 +549,8 @@ namespace engine {
Pipeline pipeline{}; Pipeline pipeline{};
std::queue<const gfx::VertexBuffer*> drawQueue{};
}; };
GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window) GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window)
@ -952,6 +971,11 @@ namespace engine {
vkDestroyInstance(pimpl->instance, nullptr); vkDestroyInstance(pimpl->instance, nullptr);
} }
void GFXDevice::drawBuffer(const gfx::VertexBuffer* vb)
{
pimpl->drawQueue.push(vb);
}
void GFXDevice::draw() void GFXDevice::draw()
{ {
VkResult res; VkResult res;
@ -995,7 +1019,6 @@ namespace engine {
renderPassInfo.pClearValues = &clearColor; renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(pimpl->commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(pimpl->commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(pimpl->commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pimpl->pipeline.handle);
VkViewport viewport{}; VkViewport viewport{};
viewport.x = 0.0f; viewport.x = 0.0f;
@ -1011,7 +1034,20 @@ namespace engine {
scissor.extent = pimpl->swapchain.extent; scissor.extent = pimpl->swapchain.extent;
vkCmdSetScissor(pimpl->commandBuffer, 0, 1, &scissor); 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); 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; VkResult res;
// get vertex attrib layout:
VkVertexInputBindingDescription bindingDescription{ };
bindingDescription.binding = 0;
bindingDescription.stride = vertexFormat.stride;
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
std::vector<VkVertexInputAttributeDescription> 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 vertShaderCode = readFile(vertShaderPath);
auto fragShaderCode = readFile(fragShaderPath); auto fragShaderCode = readFile(fragShaderPath);
INFO("Opened shader: {}", std::filesystem::path(vertShaderPath).filename().string()); INFO("Opened shader: {}", std::filesystem::path(vertShaderPath).filename().string());
@ -1080,12 +1133,13 @@ namespace engine {
VkPipelineShaderStageCreateInfo shaderStages[2] = { vertShaderStageInfo, fragShaderStageInfo }; VkPipelineShaderStageCreateInfo shaderStages[2] = { vertShaderStageInfo, fragShaderStageInfo };
// this sets "vertex attribute pointers"
VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0; vertexInputInfo.vertexBindingDescriptionCount = 1;
vertexInputInfo.pVertexBindingDescriptions = nullptr; vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
vertexInputInfo.vertexAttributeDescriptionCount = 0; vertexInputInfo.vertexAttributeDescriptionCount = attribDescs.size();
vertexInputInfo.pVertexAttributeDescriptions = nullptr; vertexInputInfo.pVertexAttributeDescriptions = attribDescs.data();
VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; 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 }; 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.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.flags = 0;
VmaAllocationCreateInfo allocInfo{}; VmaAllocationCreateInfo allocInfo{};
allocInfo.usage = VMA_MEMORY_USAGE_AUTO; 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); VkResult res = vmaCreateBuffer(pimpl->allocator, &createInfo, &allocInfo, &out->buffer, &out->allocation, nullptr);
assert(res == VK_SUCCESS); 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; return out;
} }
void GFXDevice::destroyBuffer(const gfx::BufferHandle* buffer) void GFXDevice::destroyVertexBuffer(const gfx::VertexBuffer* buffer)
{ {
vmaDestroyBuffer(pimpl->allocator, buffer->buffer, buffer->allocation); vmaDestroyBuffer(pimpl->allocator, buffer->buffer, buffer->allocation);
delete buffer; delete buffer;