403 lines
12 KiB
C++
403 lines
12 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>
|
|
#define GLM_FORCE_RADIANS
|
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
|
#define GLM_ENABLE_EXPERIMENTAL
|
|
#include <glm/glm.hpp>
|
|
#include <glm/ext/matrix_clip_space.hpp>
|
|
#include "glm/gtx/string_cast.hpp"
|
|
#include <vma/vk_mem_alloc.h>
|
|
#include "sprite.h"
|
|
#include "texture.h"
|
|
#include <misc.h>
|
|
#include <array>
|
|
#include <span>
|
|
#include <slang/slang.h>
|
|
#include <slang/slang-com-ptr.h>
|
|
|
|
inline Slang::ComPtr<slang::IGlobalSession> slangGlobalSession;
|
|
|
|
enum class PROJECTION_TYPE : uint8_t {
|
|
NONE,
|
|
ORTHOGRAPHIC_WORLD,
|
|
ORTHOGRAPHIC_WINDOW,
|
|
PERSPECTIVE_WORLD,
|
|
PERSPECTIVE_WINDOW,
|
|
COUNT,
|
|
};
|
|
|
|
struct vertex_p2_s2_st2_col4_a1_u32 {
|
|
glm::vec2 pos;
|
|
glm::vec2 scale;
|
|
glm::vec2 uv;
|
|
glm::vec4 color;
|
|
float alpha;
|
|
uint32_t textureID;
|
|
|
|
static VkVertexInputBindingDescription getBindingDescription() {
|
|
return {0, sizeof(vertex_p2_s2_st2_col4_a1_u32), VK_VERTEX_INPUT_RATE_VERTEX};
|
|
}
|
|
|
|
static std::array<VkVertexInputAttributeDescription, 6> getAttributeDescriptions() {
|
|
return {
|
|
{
|
|
{0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, pos)},
|
|
{1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, scale)},
|
|
{2, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, uv)},
|
|
{3, 0, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, color)},
|
|
{4, 0, VK_FORMAT_R32_SFLOAT, offsetof(vertex_p2_s2_st2_col4_a1_u32, alpha)},
|
|
{5, 0, VK_FORMAT_R32_UINT, offsetof(vertex_p2_s2_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 pos;
|
|
glm::vec2 scale;
|
|
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(glm::vec2 pos, glm::vec2 scale);
|
|
|
|
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_s2_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_s2_st2_col4_a1_u32> vertices) const;
|
|
|
|
[[nodiscard]] 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) const;
|
|
|
|
// Returns the resource info so the Manager can store it
|
|
void upload_texture(
|
|
int w,
|
|
int h,
|
|
const void* pixels,
|
|
VkImage *image,
|
|
VmaAllocation *allocation,
|
|
VkImageView *view,
|
|
uint32_t *descriptor_index);
|
|
|
|
template <typename T>
|
|
VkPipeline create_graphics_pipeline(
|
|
VkDevice device,
|
|
VkPipelineLayout layout,
|
|
VkFormat colorFormat,
|
|
// VkShaderModule vertShader,
|
|
// VkShaderModule fragShader,
|
|
VkPrimitiveTopology topology,
|
|
bool enableBlending)
|
|
{
|
|
|
|
auto slangTargets{ std::to_array<slang::TargetDesc>({ {
|
|
.format = SLANG_SPIRV,
|
|
.profile = slangGlobalSession->findProfile("spirv_1_4")
|
|
} })};
|
|
auto slangOptions{ std::to_array<slang::CompilerOptionEntry>({ {
|
|
slang::CompilerOptionName::EmitSpirvDirectly,
|
|
{slang::CompilerOptionValueKind::Int, 1}
|
|
} })};
|
|
slang::SessionDesc slangSessionDesc{
|
|
.targets = slangTargets.data(),
|
|
.targetCount = SlangInt(slangTargets.size()),
|
|
.defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR,
|
|
.compilerOptionEntries = slangOptions.data(),
|
|
.compilerOptionEntryCount = uint32_t(slangOptions.size())
|
|
};
|
|
Slang::ComPtr<slang::ISession> slangSession;
|
|
slangGlobalSession->createSession(slangSessionDesc, slangSession.writeRef());
|
|
|
|
Slang::ComPtr<slang::IModule> slangModule{
|
|
slangSession->loadModuleFromSource("triangle", "shaders/shader.slang", nullptr, nullptr)
|
|
};
|
|
Slang::ComPtr<ISlangBlob> spirv;
|
|
slangModule->getTargetCode(0, spirv.writeRef());
|
|
|
|
VkShaderModuleCreateInfo shaderModuleCI{
|
|
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
|
.codeSize = spirv->getBufferSize(),
|
|
.pCode = (uint32_t*)spirv->getBufferPointer()
|
|
};
|
|
VkShaderModule shaderModule{};
|
|
vkCreateShaderModule(device, &shaderModuleCI, nullptr, &shaderModule);
|
|
|
|
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 ---
|
|
std::vector<VkPipelineShaderStageCreateInfo> shaderStages{
|
|
{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
|
.module = shaderModule, .pName = "main"},
|
|
{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
.module = shaderModule, .pName = "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_COUNTER_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 = (uint32_t) shaderStages.size(),
|
|
.pStages = shaderStages.data(),
|
|
.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
|