Aeon Engine c550894
AeonGames Open Source Game Engine
Loading...
Searching...
No Matches
OpenGLPipeline.cpp
1/*
2Copyright (C) 2016-2021,2025,2026 Rodrigo Jose Hernandez Cordoba
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16#include "OpenGLPipeline.hpp"
17#include "OpenGLFunctions.hpp"
18#include "aeongames/CRC.hpp"
19#include <vector>
20#include <algorithm>
21#include <cassert>
22
23namespace AeonGames
24{
25
27 mOpenGLRenderer{aOpenGLPipeline.mOpenGLRenderer}
28 {
29 std::swap ( mPipeline, aOpenGLPipeline.mPipeline );
30 std::swap ( mProgramId, aOpenGLPipeline.mProgramId );
31 mAttributes.swap ( aOpenGLPipeline.mAttributes );
32 mUniformBlocks.swap ( aOpenGLPipeline.mUniformBlocks );
33 mSamplerLocations.swap ( aOpenGLPipeline.mSamplerLocations );
34 }
35
36 static const std::unordered_map<ShaderType, const GLenum> ShaderTypeToGLShaderType
37 {
38 { VERT, GL_VERTEX_SHADER },
39 { FRAG, GL_FRAGMENT_SHADER },
40 { COMP, GL_COMPUTE_SHADER },
41 { TESC, GL_TESS_CONTROL_SHADER },
42 { TESE, GL_TESS_EVALUATION_SHADER },
43 { GEOM, GL_GEOMETRY_SHADER }
44 };
45
46 OpenGLPipeline::OpenGLPipeline ( const OpenGLRenderer& aOpenGLRenderer, const Pipeline& aPipeline ) :
47 mOpenGLRenderer{aOpenGLRenderer}, mPipeline{&aPipeline}
48 {
49 mProgramId = glCreateProgram();
50 OPENGL_CHECK_ERROR_THROW;
51 GLint compile_status;
52 GLint link_status;
53
54 std::array<std::string_view, ShaderType::COUNT> shader_codes =
55 {
56 aPipeline.GetShaderCode ( VERT ),
57 aPipeline.GetShaderCode ( FRAG ),
58 aPipeline.GetShaderCode ( COMP ),
59 aPipeline.GetShaderCode ( TESC ),
60 aPipeline.GetShaderCode ( TESE ),
61 aPipeline.GetShaderCode ( GEOM )
62 };
63
64 std::array<uint32_t, ShaderType::COUNT> shader_ids =
65 {
66 shader_codes.at ( VERT ).empty() ? 0 : glCreateShader ( ShaderTypeToGLShaderType.at ( VERT ) ),
67 shader_codes.at ( FRAG ).empty() ? 0 : glCreateShader ( ShaderTypeToGLShaderType.at ( FRAG ) ),
68 shader_codes.at ( COMP ).empty() ? 0 : glCreateShader ( ShaderTypeToGLShaderType.at ( COMP ) ),
69 shader_codes.at ( TESC ).empty() ? 0 : glCreateShader ( ShaderTypeToGLShaderType.at ( TESC ) ),
70 shader_codes.at ( TESE ).empty() ? 0 : glCreateShader ( ShaderTypeToGLShaderType.at ( TESE ) ),
71 shader_codes.at ( GEOM ).empty() ? 0 : glCreateShader ( ShaderTypeToGLShaderType.at ( GEOM ) )
72 };
73 OPENGL_CHECK_ERROR_THROW;
74
75 //-------------------------
76 for ( size_t i = 0; i < shader_codes.size(); ++i )
77 {
78 if ( shader_ids.at ( i ) == 0 )
79 {
80 continue;
81 }
82 const auto* source_ptr = reinterpret_cast<const GLchar *> ( shader_codes.at ( i ).data() );
83 auto source_len = static_cast<GLint> ( shader_codes.at ( i ).length() );
84
85 glShaderSource (
86 shader_ids.at ( i ),
87 1,
88 &source_ptr,
89 &source_len );
90 OPENGL_CHECK_ERROR_THROW;
91
92 glCompileShader ( shader_ids.at ( i ) );
93 OPENGL_CHECK_ERROR_THROW;
94 glGetShaderiv ( shader_ids.at ( i ), GL_COMPILE_STATUS, &compile_status );
95 OPENGL_CHECK_ERROR_THROW;
96 if ( compile_status != GL_TRUE )
97 {
98 GLint info_log_len;
99 glGetShaderiv ( shader_ids.at ( i ), GL_INFO_LOG_LENGTH, &info_log_len );
100 OPENGL_CHECK_ERROR_THROW;
101 std::string log_string;
102 log_string.resize ( info_log_len );
103 if ( info_log_len > 1 )
104 {
105 glGetShaderInfoLog ( shader_ids.at ( i ), info_log_len, nullptr, const_cast<GLchar*> ( log_string.data() ) );
106 OPENGL_CHECK_ERROR_THROW;
107 std::cout << shader_codes.at ( i ) << std::endl;
108 std::cout << log_string << std::endl;
109 std::cout << LogLevel::Error << log_string << std::endl;
110 throw std::runtime_error ( log_string.c_str() );
111 }
112 log_string = ShaderTypeToString.at ( static_cast<ShaderType> ( i ) );
113 std::cout << LogLevel::Error << "Error Compiling Shaders." << std::endl;
114 throw std::runtime_error ( "Error Compiling Shaders." );
115 }
116 glAttachShader ( mProgramId, shader_ids.at ( i ) );
117 OPENGL_CHECK_ERROR_THROW;
118 }
119 //-------------------------
120
121 glLinkProgram ( mProgramId );
122 OPENGL_CHECK_ERROR_THROW;
123 glGetProgramiv ( mProgramId, GL_LINK_STATUS, &link_status );
124 OPENGL_CHECK_ERROR_THROW;
125 if ( link_status != GL_TRUE )
126 {
127 GLint info_log_len;
128 glGetProgramiv ( mProgramId, GL_INFO_LOG_LENGTH, &info_log_len );
129 OPENGL_CHECK_ERROR_THROW;
130 std::string log_string;
131 log_string.resize ( info_log_len );
132 if ( info_log_len > 1 )
133 {
134 glGetProgramInfoLog ( mProgramId, info_log_len, nullptr, const_cast<GLchar*> ( log_string.data() ) );
135 for ( const auto& shader_code : shader_codes )
136 {
137 if ( shader_code.empty() )
138 {
139 continue;
140 }
141 std::cout << shader_code << std::endl;
142 }
143 std::cout << log_string << std::endl;
144 OPENGL_CHECK_ERROR_THROW;
145 }
146 }
147 for ( const auto& shader_id : shader_ids )
148 {
149 if ( shader_id == 0 )
150 {
151 continue;
152 }
153 glDetachShader ( mProgramId, shader_id );
154 OPENGL_CHECK_ERROR_THROW;
155 glDeleteShader ( shader_id );
156 OPENGL_CHECK_ERROR_THROW;
157 }
158 ReflectAttributes();
159 ReflectUniforms();
160 }
161
162 void OpenGLPipeline::ReflectAttributes()
163 {
164 mAttributes.clear();
165 GLint active_attribute_count{};
166 GLchar name[256] {};
167 GLsizei length{};
168 GLint size{};
169 GLenum type{};
170 GLint location{};
171 glGetProgramiv ( mProgramId, GL_ACTIVE_ATTRIBUTES, &active_attribute_count );
172 OPENGL_CHECK_ERROR_THROW;
173 mAttributes.reserve ( active_attribute_count );
174 for ( GLint i = 0; i < active_attribute_count; ++i )
175 {
176 glGetActiveAttrib ( mProgramId, i, sizeof ( name ), &length, &size, &type, name );
177 OPENGL_CHECK_ERROR_THROW;
178 location = glGetAttribLocation ( mProgramId, name );
179 OPENGL_CHECK_ERROR_THROW;
180 // Skip reserved attributes
181 if ( location < 0 )
182 {
183 continue;
184 }
185 const uint32_t name_crc{crc32i ( name, length ) };
186 auto it = std::lower_bound ( mAttributes.begin(), mAttributes.end(), name_crc,
187 [] ( const OpenGLVariable & a, const uint32_t b )
188 {
189 return a.name < b;
190 } );
191 mAttributes.insert ( it, { name_crc, {location}, size, type } );
192 }
193 for ( const auto& attribute : mAttributes )
194 {
195 std::cout << LogLevel::Debug << "Attribute: " << attribute.name << " (crc: " << std::hex << attribute.name << std::dec << ", location: " << attribute.location << ", size: " << attribute.size << ", type: " << attribute.type << ")" << std::endl;
196 }
197 }
198
199 void OpenGLPipeline::ReflectUniforms()
200 {
201 mSamplerLocations.clear();
202 mUniformBlocks.clear();
203 GLint block_uniform_count{};
204 GLint uniform_block_count = 0;
205 glGetProgramiv ( mProgramId, GL_ACTIVE_UNIFORM_BLOCKS, &uniform_block_count );
206 OPENGL_CHECK_ERROR_THROW;
207
208 GLchar name[256];
209 GLint indices[64];
210 GLsizei length{};
211 GLint size{};
212 GLenum type{};
213 GLint binding_offset_or_location{};
214
215 mUniformBlocks.reserve ( uniform_block_count );
216 for ( GLint i = 0; i < uniform_block_count; ++i )
217 {
218 glGetActiveUniformBlockName ( mProgramId, i, sizeof ( name ), &length, name );
219 OPENGL_CHECK_ERROR_THROW;
220 glGetActiveUniformBlockiv ( mProgramId, i, GL_UNIFORM_BLOCK_DATA_SIZE, &size );
221 OPENGL_CHECK_ERROR_THROW;
222 glGetActiveUniformBlockiv ( mProgramId, i, GL_UNIFORM_BLOCK_BINDING, &binding_offset_or_location );
223 OPENGL_CHECK_ERROR_THROW;
224 glGetActiveUniformBlockiv ( mProgramId, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &block_uniform_count );
225 OPENGL_CHECK_ERROR_THROW;
226 assert ( block_uniform_count <= 64 );
227
228 const uint32_t name_crc{crc32i ( name, length ) };
229 auto it = std::lower_bound ( mUniformBlocks.begin(), mUniformBlocks.end(), name_crc,
230 [] ( const OpenGLUniformBlock & a, const uint32_t b )
231 {
232 return a.name < b;
233 } );
234 mUniformBlocks.insert ( it, { name_crc, size, binding_offset_or_location } );
235
236 it->uniforms.reserve ( block_uniform_count );
237 if ( block_uniform_count > 0 )
238 {
239 glGetActiveUniformBlockiv ( mProgramId, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, indices );
240 OPENGL_CHECK_ERROR_THROW;
241 for ( GLint j = 0; j < block_uniform_count; ++j )
242 {
243 GLuint uniform_index = static_cast<GLuint> ( indices[ static_cast<size_t> ( j ) ] );
244 glGetActiveUniform ( mProgramId, uniform_index, sizeof ( name ), &length, &size, &type, name );
245 OPENGL_CHECK_ERROR_THROW;
246
247 glGetActiveUniformsiv ( mProgramId, 1, &uniform_index, GL_UNIFORM_OFFSET, &binding_offset_or_location );
248 OPENGL_CHECK_ERROR_THROW;
249
250 uint32_t name_crc{crc32i ( name, length ) };
251 auto il = std::lower_bound ( it->uniforms.begin(), it->uniforms.end(), name_crc,
252 [] ( const OpenGLVariable & a, const uint32_t b )
253 {
254 return a.name < b;
255 } );
256 it->uniforms.insert ( il, { name_crc, {binding_offset_or_location}, size, type } );
257 }
258 }
259 }
260
261 GLint uniform_count;
262 GLint sampler_count{0};
263 glGetProgramiv ( mProgramId, GL_ACTIVE_UNIFORMS, &uniform_count );
264 OPENGL_CHECK_ERROR_THROW;
265 for ( GLint i = 0; i < uniform_count; ++i )
266 {
267 glGetActiveUniform ( mProgramId, i, sizeof ( name ), &length, &size, &type, name );
268 OPENGL_CHECK_ERROR_THROW;
269 sampler_count += ( type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE || type == GL_SAMPLER_3D ||
270 type == GL_SAMPLER_2D_ARRAY || type == GL_SAMPLER_2D_SHADOW || type == GL_SAMPLER_CUBE_SHADOW ) ? 1 : 0;
271 }
272 mSamplerLocations.reserve ( sampler_count );
273 for ( GLint i = 0; i < uniform_count; ++i )
274 {
275 glGetActiveUniform ( mProgramId, i, sizeof ( name ), &length, &size, &type, name );
276 OPENGL_CHECK_ERROR_THROW;
277 if ( type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE || type == GL_SAMPLER_3D ||
278 type == GL_SAMPLER_2D_ARRAY || type == GL_SAMPLER_2D_SHADOW || type == GL_SAMPLER_CUBE_SHADOW )
279 {
280 binding_offset_or_location = glGetUniformLocation ( mProgramId, name );
281 OPENGL_CHECK_ERROR_THROW;
282 uint32_t name_crc{crc32i ( name, length ) };
283 auto il = std::lower_bound ( mSamplerLocations.begin(), mSamplerLocations.end(), name_crc,
284 [] ( const OpenGLSamplerLocation & a, const uint32_t b )
285 {
286 return a.name < b;
287 } );
288 mSamplerLocations.insert ( il, { name_crc, binding_offset_or_location } );
289 }
290 }
291
292 for ( const auto& sampler : mSamplerLocations )
293 {
294 std::cout << LogLevel::Debug << "Sampler: " << sampler.name << " (crc: " << std::hex << sampler.name << std::dec << ", location: " << sampler.location << ")" << std::endl;
295 }
296 for ( const auto& block : mUniformBlocks )
297 {
298 std::cout << LogLevel::Debug << "Uniform Block: " << block.name << " (crc: " << std::hex << block.name << std::dec << ", size: " << block.size << ", binding: " << block.binding << ", active uniforms: " << block.uniforms.size() << ")" << std::endl;
299 for ( const auto& uniform : block.uniforms )
300 {
301 std::cout << LogLevel::Debug << "\tUniform: " << uniform.name << " (crc: " << std::hex << uniform.name << std::dec << ", offset: " << uniform.offset << ", size: " << uniform.size << ", type: " << uniform.type << ")" << std::endl;
302 }
303 }
304 }
305
306 OpenGLPipeline::~OpenGLPipeline()
307 {
308 if ( glIsProgram ( mProgramId ) )
309 {
310 OPENGL_CHECK_ERROR_NO_THROW;
311 glDeleteProgram ( mProgramId );
312 OPENGL_CHECK_ERROR_NO_THROW;
313 mProgramId = 0;
314 }
315 OPENGL_CHECK_ERROR_NO_THROW;
316 }
317
319 {
320 return mProgramId;
321 }
322
323 const std::vector<OpenGLVariable>& OpenGLPipeline::GetVertexAttributes() const
324 {
325 return mAttributes;
326 }
327
328 const GLuint OpenGLPipeline::GetSamplerLocation ( uint32_t name_hash ) const
329 {
330 auto it = std::lower_bound ( mSamplerLocations.begin(), mSamplerLocations.end(), name_hash,
331 [] ( const OpenGLSamplerLocation & a, const uint32_t b )
332 {
333 return a.name < b;
334 } );
335 if ( it == mSamplerLocations.end() || it->location < 0 )
336 {
337 return 0;
338 }
339 return it->location;
340 }
341
343 {
344 auto it = std::lower_bound ( mUniformBlocks.begin(), mUniformBlocks.end(), name,
345 [] ( const OpenGLUniformBlock & a, const uint32_t b )
346 {
347 return a.name < b;
348 } );
349 if ( it == mUniformBlocks.end() )
350 {
351 return nullptr;
352 }
353 return &*it;
354 }
355}
OpenGLPipeline(const OpenGLRenderer &aOpenGLRenderer, const Pipeline &aPipeline)
Construct from a renderer and pipeline resource.
GLint GetProgramId() const
Get the linked shader program identifier.
const OpenGLUniformBlock * GetUniformBlock(uint32_t name) const
Get a uniform block by its name hash, or nullptr if not found.
const std::vector< OpenGLVariable > & GetVertexAttributes() const
Get the reflected vertex attribute descriptions.
const GLuint GetSamplerLocation(uint32_t name_hash) const
Get the uniform location of a sampler by its name hash.
OpenGL rendering backend implementing the Renderer interface.
Rendering pipeline resource.
Definition Pipeline.hpp:122
DLL const std::string_view GetShaderCode(ShaderType aType) const
Get the shader source code for the given shader stage.
Definition Pipeline.cpp:204
<- This is here just for the literals
Definition AABB.hpp:31
uint32_t crc32i(const char *message, size_t size, uint32_t previous_crc)
Compute the CRC32 of a given message, continuing from a previous CRC value.
Definition CRC.cpp:27
const std::unordered_map< ShaderType, const char * > ShaderTypeToString
Map from ShaderType enum values to human-readable string names.
Definition Pipeline.hpp:108
ShaderType
Shader stage types.
Definition Pipeline.hpp:96
@ VERT
Vertex shader.
Definition Pipeline.hpp:97
@ FRAG
Fragment shader.
Definition Pipeline.hpp:98
@ GEOM
Geometry shader.
Definition Pipeline.hpp:102
@ TESC
Tessellation control shader.
Definition Pipeline.hpp:100
@ TESE
Tessellation evaluation shader.
Definition Pipeline.hpp:101
@ COMP
Compute shader.
Definition Pipeline.hpp:99
@ Error
Error conditions.
Definition LogLevel.hpp:33
@ Debug
Detailed diagnostic information.
Definition LogLevel.hpp:29
Maps a sampler name hash to its OpenGL uniform location.
Describes an OpenGL uniform block with its binding and member variables.