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 <cstddef>
#include <vector>
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<VertexAttribDescription> attributeDescriptions;
};
// handles (incomplete types)
struct VertexBuffer;
}

View File

@ -2,36 +2,13 @@
#include "engine_api.h"
#include "gfx.hpp"
#include <memory>
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();

View File

@ -5,6 +5,11 @@
#include "resource_manager.hpp"
#include "log.hpp"
#include <glm/glm.hpp>
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<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()
{
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 */

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
#ifdef ENGINE_BUILD_OPENGL

View File

@ -25,6 +25,7 @@
#include <fstream>
#include <filesystem>
#include <optional>
#include <queue>
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<char> readFile(const std::string& filename)
@ -532,6 +549,8 @@ namespace engine {
Pipeline pipeline{};
std::queue<const gfx::VertexBuffer*> 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<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 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;