v/renderer/renderer.h

363 lines
10 KiB
C++

//
// Created by Vicente Ferrari Smith on 13.02.26.
//
#ifndef V_RENDERER_H
#define V_RENDERER_H
#include "init.h"
#include <volk/volk.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <vma/vk_mem_alloc.h>
#include "sprite.h"
#include "texture.h"
#include <misc.h>
#include <array>
#include <span>
enum class PROJECTION_TYPE : uint8_t {
NONE,
ORTHOGRAPHIC_WORLD,
ORTHOGRAPHIC_WINDOW,
PERSPECTIVE_WORLD,
PERSPECTIVE_WINDOW,
COUNT,
};
struct vertex_p2_st2_col4_a1_u32 {
glm::vec2 pos;
glm::vec2 st;
glm::vec4 col;
float alpha;
uint32_t textureID;
static VkVertexInputBindingDescription getBindingDescription() {
return {0, sizeof(vertex_p2_st2_col4_a1_u32), VK_VERTEX_INPUT_RATE_VERTEX};
}
static std::array<VkVertexInputAttributeDescription, 5> getAttributeDescriptions() {
return {
{
{0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_st2_col4_a1_u32, pos)},
{1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_st2_col4_a1_u32, st)},
{2, 0, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(vertex_p2_st2_col4_a1_u32, col)},
{3, 0, VK_FORMAT_R32_SFLOAT, offsetof(vertex_p2_st2_col4_a1_u32, alpha)},
{4, 0, VK_FORMAT_R32_UINT, offsetof(vertex_p2_st2_col4_a1_u32, textureID)},
}
};
}
};
// commands
enum class PipelineType : uint8_t {
None,
TexturedQuad,
ColoredQuad,
Line,
Text,
Chunk
};
struct TexturedQuadCmd {
glm::vec2 position;
glm::vec2 size;
glm::vec2 uvMin;
glm::vec2 uvMax;
glm::vec4 color;
uint16_t textureID;
};
struct ColoredQuadCmd {
glm::vec2 position;
glm::vec2 size;
glm::vec4 color;
};
struct LineCmd {
glm::vec2 start;
glm::vec2 end;
glm::vec4 color;
};
// struct TextCmd {
// Font* font;
// std::string text;
// glm::vec2 position;
// glm::vec4 color;
// };
struct ChunkCmd {
VkBuffer vertexBuffer;
VkBuffer indexBuffer;
uint32_t indexCount;
};
struct SortKey {
uint16_t depth; // world Z or Y-sorted depth
uint16_t materialID; // texture sheet, font atlas, etc.
uint8_t pipeline; // PipelineType
bool operator<(const SortKey& b) const;
};
struct RenderCommand {
SortKey key;
PipelineType pipeline;
union {
TexturedQuadCmd textured_quad;
ColoredQuadCmd colored_quad;
LineCmd line;
// TextCmd text;
ChunkCmd chunk;
};
};
////////////////////////////////////////////////////////////////////////////////////////////////
struct AllocatedBuffer {
VkBuffer buffer;
VmaAllocation allocation;
VmaAllocationInfo info;
};
struct GPUMeshBuffers {
AllocatedBuffer indexBuffer;
AllocatedBuffer vertexBuffer;
VkDeviceAddress vertexBufferAddress;
};
struct Renderer {
std::vector<RenderCommand> commands{};
VkDescriptorSetLayout descriptor_set_layout{};
VkPipelineLayout pipelineLayout{};
VkPipeline textured_quad_pipeline{};
VkPipeline colored_quad_pipeline{};
VkPipeline line_pipeline{};
VkPipeline text_pipeline{};
VkPipeline chunk_pipeline{};
VkDescriptorSet set{};
VkSampler defaultSampler{};
uint32_t nextTextureSlot = 0;
struct Frame {
VkCommandPool commandPool{};
VkCommandBuffer command_buffer{};
VkSemaphore imageAvailable{};
VkFence in_flight_fence{};
AllocatedBuffer vertexBuffer{};
};
std::vector<Frame> frames;
uint32_t currentFrame = 0;
VkDescriptorPool descriptorPool{};
std::vector<VkDescriptorSet> textureSets{};
void begin_frame();
void end_frame();
void flush();
void submit_sprite(glm::vec2 pos, const sprite_t &sprite);
void submit_quad();
explicit Renderer(GLFWwindow *window);
void create_pipeline_layout();
void createFrameResources();
void create_default_sampler();
void recordCommandBuffer(
VkCommandBuffer cmd,
VkImage image,
VkImageView imageView,
VkExtent2D extent,
VkImageLayout oldLayout,
const Frame &frame,
const std::vector<vertex_p2_st2_col4_a1_u32> &vertices) const;
void immediate_submit(std::function<void(VkCommandBuffer)>&& func) const;
void transition_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout) const;
VkImageView create_image_view(VkImage image, VkFormat format) const;
AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage);
void destroy_buffer(const AllocatedBuffer& buffer);
// GPUMeshBuffers uploadMesh(std::span<uint32_t> indices, std::span<vertex_p2_st2_col4_a1_u32> vertices);
void upload_vertex_buffer(
VkCommandBuffer cmd,
const Frame &frame,
std::span<const vertex_p2_st2_col4_a1_u32> vertices) const;
VkPipeline get_pipeline(PipelineType type) const;
// void bind_material(VkCommandBuffer cmd, uint16_t materialID);
void create_descriptor_pool();
void update_bindless_slot(uint32_t slot, VkImageView view, VkSampler sampler);
// Returns the resource info so the Manager can store it
Texture upload_texture(int w, int h, void* pixels);
template <typename T>
VkPipeline create_graphics_pipeline(
VkDevice device,
VkPipelineLayout layout,
VkFormat colorFormat,
// VkShaderModule vertShader,
// VkShaderModule fragShader,
VkPrimitiveTopology topology,
bool enableBlending)
{
auto vsCode = loadFile("shaders/triangle.vert.spv");
auto fsCode = loadFile("shaders/triangle.frag.spv");
VkShaderModuleCreateInfo smci{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO
};
smci.codeSize = vsCode.size();
smci.pCode = reinterpret_cast<uint32_t*>(vsCode.data());
VkShaderModule vs;
vkCreateShaderModule(device, &smci, nullptr, &vs);
smci.codeSize = fsCode.size();
smci.pCode = reinterpret_cast<uint32_t*>(fsCode.data());
VkShaderModule fs;
vkCreateShaderModule(device, &smci, nullptr, &fs);
// --- Shaders ---
VkPipelineShaderStageCreateInfo stages[2] = {
{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr,
0,
VK_SHADER_STAGE_VERTEX_BIT,
vs,
"main"
},
{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr,
0,
VK_SHADER_STAGE_FRAGMENT_BIT,
fs,
"main"
}
};
// --- Vertex Input (Generic) ---
auto binding = T::getBindingDescription();
auto attrs = T::getAttributeDescriptions();
// --- Vertex Input (Matching our vertex_p2_st2_col4 struct) ---
VkPipelineVertexInputStateCreateInfo vi{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &binding,
.vertexAttributeDescriptionCount = attrs.size(),
.pVertexAttributeDescriptions = attrs.data(),
};
// --- Input Assembly (Changes based on Topology parameter) ---
VkPipelineInputAssemblyStateCreateInfo ia{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
ia.topology = topology;
// --- Blending (Changes based on enableBlending parameter) ---
VkPipelineColorBlendAttachmentState colorBlend{
.blendEnable = enableBlending ? VK_TRUE : VK_FALSE,
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = 0xF
};
// --- Boilerplate (Standard 2D Defaults) ---
VkPipelineViewportStateCreateInfo vp{
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
nullptr,
0,
1,
nullptr,
1,
nullptr
};
VkPipelineRasterizationStateCreateInfo rs{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
nullptr,
0,
0,
0,
VK_POLYGON_MODE_FILL,
VK_CULL_MODE_NONE,
VK_FRONT_FACE_CLOCKWISE,
0,
0,
0,
0,
1.0f
};
VkPipelineMultisampleStateCreateInfo ms{
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
nullptr,
0,
VK_SAMPLE_COUNT_1_BIT
};
VkPipelineColorBlendStateCreateInfo cb{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
nullptr,
0,
0,
VK_LOGIC_OP_AND,
1,
&colorBlend
};
VkDynamicState dyns[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo ds{
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
nullptr,
0,
2,
dyns
};
VkPipelineRenderingCreateInfo rci{
VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
nullptr,
0,
1,
&colorFormat
};
VkGraphicsPipelineCreateInfo gpci{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = &rci,
.stageCount = 2,
.pStages = stages,
.pVertexInputState = &vi,
.pInputAssemblyState = &ia,
.pViewportState = &vp,
.pRasterizationState = &rs,
.pMultisampleState = &ms,
.pColorBlendState = &cb,
.pDynamicState = &ds,
.layout = layout
};
VkPipeline pipeline;
vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &pipeline);
return pipeline;
}
};
#endif //V_RENDERER_H