Flint Engine
Modular C++20 2D game engine powered by SDL3
Loading...
Searching...
No Matches
pipeline-base.h
1// SPDX-License-Identifier: MIT
2// SPDX-FileCopyrightText: 2025-2026 Manuel Osorio
3#pragma once
4#include "flint/core/log.h"
5#include "flint/graphics/shaders.h"
6
7#include <SDL3/SDL_gpu.h>
8
9namespace Flint {
17 Uint32 vertexStride = sizeof(float) * 4;
19 bool enableBlending = true;
20 SDL_GPUBlendFactor srcColorBlend = SDL_GPU_BLENDFACTOR_SRC_ALPHA;
21 SDL_GPUBlendFactor dstColorBlend = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
22 SDL_GPUBlendFactor srcAlphaBlend = SDL_GPU_BLENDFACTOR_ONE;
23 SDL_GPUBlendFactor dstAlphaBlend = SDL_GPU_BLENDFACTOR_ZERO;
24
25 bool enableDepthTest = false;
26 bool enableDepthWrite = false;
27
28 SDL_GPUCullMode cullMode = SDL_GPU_CULLMODE_NONE;
29 SDL_GPUFillMode fillMode = SDL_GPU_FILLMODE_FILL;
30
31 SDL_GPUTextureFormat colorFormat = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
32 };
33
42 class Pipeline {
43 public:
49 Pipeline(SDL_GPUDevice *device)
50 : m_device(device) {
51 }
52
53 Pipeline() = default;
54
55 virtual ~Pipeline() {
57 }
58
70 virtual bool initialize(Shader *shader, const PipelineConfig &config) {
71 if (!shader || !shader->getVertexShader() || !shader->getFragmentShader()) {
72 FLINT_ENGINE_ERROR("Pipeline: Invalid shader provided");
73 return false;
74 }
75
76 m_shader = shader;
77
78 SDL_GPUVertexBufferDescription vertexBinding = {};
79 vertexBinding.slot = 0;
80 vertexBinding.pitch = config.vertexStride;
81 vertexBinding.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
82
83 SDL_GPUVertexAttribute vertexAttributes[2] = {};
84 vertexAttributes[0].location = 0;
85 vertexAttributes[0].buffer_slot = 0;
86 vertexAttributes[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2;
87 vertexAttributes[0].offset = 0;
88
89 vertexAttributes[1].location = 1;
90 vertexAttributes[1].buffer_slot = 0;
91 vertexAttributes[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2;
92 vertexAttributes[1].offset = sizeof(float) * 2;
93
94 SDL_GPUVertexInputState vertexInputState = {};
95 vertexInputState.num_vertex_buffers = 1;
96 vertexInputState.vertex_buffer_descriptions = &vertexBinding;
97 vertexInputState.num_vertex_attributes = 2;
98 vertexInputState.vertex_attributes = vertexAttributes;
99
100 SDL_GPUGraphicsPipelineCreateInfo pipelineInfo{};
101 pipelineInfo.vertex_shader = m_shader->getVertexShader();
102 pipelineInfo.fragment_shader = m_shader->getFragmentShader();
103 pipelineInfo.vertex_input_state = vertexInputState;
104 pipelineInfo.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
105
106 pipelineInfo.rasterizer_state.cull_mode = config.cullMode;
107 pipelineInfo.rasterizer_state.fill_mode = config.fillMode;
108 pipelineInfo.rasterizer_state.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE;
109
110 pipelineInfo.depth_stencil_state.enable_depth_test = config.enableDepthTest;
111 pipelineInfo.depth_stencil_state.enable_depth_write = config.enableDepthWrite;
112
113 SDL_GPUColorTargetDescription colorTarget = {};
114 colorTarget.format = config.colorFormat;
115 colorTarget.blend_state.enable_blend = config.enableBlending;
116 colorTarget.blend_state.src_color_blendfactor = config.srcColorBlend;
117 colorTarget.blend_state.dst_color_blendfactor = config.dstColorBlend;
118 colorTarget.blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD;
119 colorTarget.blend_state.src_alpha_blendfactor = config.srcAlphaBlend;
120 colorTarget.blend_state.dst_alpha_blendfactor = config.dstAlphaBlend;
121 colorTarget.blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD;
122 colorTarget.blend_state.color_write_mask = 0xF;
123
124 pipelineInfo.target_info.num_color_targets = 1;
125 pipelineInfo.target_info.color_target_descriptions = &colorTarget;
126 pipelineInfo.target_info.depth_stencil_format = SDL_GPU_TEXTUREFORMAT_INVALID;
127
128 if (!configurePipeline(pipelineInfo)) {
129 return false;
130 }
131
132 m_pipeline = SDL_CreateGPUGraphicsPipeline(m_device, &pipelineInfo);
133 if (!m_pipeline) {
134 FLINT_ENGINE_ERROR("Failed to create graphics pipeline: {}", SDL_GetError());
135 return false;
136 }
137
138 return true;
139 }
140
146 void bind(SDL_GPURenderPass *renderPass) {
147 if (m_pipeline) {
148 SDL_BindGPUGraphicsPipeline(renderPass, m_pipeline);
149 }
150 }
151
155 SDL_GPUGraphicsPipeline *getPipeline() const {
156 return m_pipeline;
157 }
158
162 Shader *getShader() const {
163 return m_shader;
164 }
165
166 protected:
177 virtual bool configurePipeline(SDL_GPUGraphicsPipelineCreateInfo &pipelineInfo) {
178 return true;
179 }
180
184 virtual void cleanup() {
185 if (m_pipeline) {
186 SDL_ReleaseGPUGraphicsPipeline(m_device, m_pipeline);
187 m_pipeline = nullptr;
188 }
189 }
190
191 SDL_GPUDevice *m_device = nullptr;
192 SDL_GPUGraphicsPipeline *m_pipeline = nullptr;
193 Shader *m_shader = nullptr;
194 };
195}
void bind(SDL_GPURenderPass *renderPass)
Bind this pipeline to the active render pass.
virtual void cleanup()
Release the SDL GPU pipeline object.
Shader * getShader() const
Return the shader associated with this pipeline.
virtual bool configurePipeline(SDL_GPUGraphicsPipelineCreateInfo &pipelineInfo)
Subclass hook to customize the pipeline create-info before submission.
SDL_GPUGraphicsPipeline * getPipeline() const
Return the raw SDL GPU graphics pipeline handle.
Pipeline(SDL_GPUDevice *device)
Construct a pipeline bound to device.
virtual bool initialize(Shader *shader, const PipelineConfig &config)
Create the underlying SDL GPU graphics pipeline.
Base class for GPU graphics pipelines.
SDL_GPUShader * getVertexShader() const
Return the vertex shader handle tracked by this object (nullptr if absent).
Definition shaders.h:173
SDL_GPUShader * getFragmentShader() const
Return the fragment shader handle tracked by this object (nullptr if absent).
Definition shaders.h:178
Owns a pair of SDL GPU shaders (vertex + fragment) tied to one SDL_GPUDevice.
Definition shaders.h:86
Configuration parameters for Pipeline::initialize().