// // Created by Vicente Ferrari Smith on 13.02.26. // #ifndef V_RENDERER_H #define V_RENDERER_H #include "init.h" #include #include #define GLM_FORCE_RADIANS #define GLM_FORCE_DEPTH_ZERO_TO_ONE #define GLM_ENABLE_EXPERIMENTAL #include #include #include "glm/gtx/string_cast.hpp" #include #include "sprite.h" #include "texture.h" #include #include #include #include #include inline Slang::ComPtr 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 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 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 frames; uint32_t currentFrame = 0; VkDescriptorPool descriptorPool{}; std::vector 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 &vertices) const; void immediate_submit(std::function&& 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 indices, std::span vertices); void upload_vertex_buffer( VkCommandBuffer cmd, const Frame &frame, std::span 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 VkPipeline create_graphics_pipeline( VkDevice device, VkPipelineLayout layout, VkFormat colorFormat, // VkShaderModule vertShader, // VkShaderModule fragShader, VkPrimitiveTopology topology, bool enableBlending) { auto slangTargets{ std::to_array({ { .format = SLANG_SPIRV, .profile = slangGlobalSession->findProfile("spirv_1_4") } })}; auto slangOptions{ std::to_array({ { 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 slangSession; slangGlobalSession->createSession(slangSessionDesc, slangSession.writeRef()); Slang::ComPtr slangModule{ slangSession->loadModuleFromSource("triangle", "shaders/shader.slang", nullptr, nullptr) }; Slang::ComPtr 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(vsCode.data()); // VkShaderModule vs; // vkCreateShaderModule(device, &smci, nullptr, &vs); smci.codeSize = fsCode.size(); smci.pCode = reinterpret_cast(fsCode.data()); // VkShaderModule fs; // vkCreateShaderModule(device, &smci, nullptr, &fs); // --- Shaders --- std::vector 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