Skip to main content

OpenGLBackend.ixx File

OpenGL backend for render-pass execution and indexed draw submission. More...

Included Headers

#include <glad/gl.h> #include <GLFW/glfw3.h> #include <memory> #include <cassert> #include <utility> #include <type_traits> #include <span> #include "helios-opengl-config.h" #include <optional> #include <helios.engine.runtime.world.EngineWorld> #include <helios.opengl.types> #include <helios.engine.scene.components> #include <helios.engine.util.Colors> #include <helios.math> #include <helios.engine.rendering.common.types> #include <helios.engine.rendering.viewport> #include <helios.engine.rendering.material> #include <helios.engine.util.log> #include <helios.engine.rendering.viewport.ViewportEntity> #include <helios.opengl.OpenGLUniformWriter> #include <helios.opengl.components> #include <helios.engine.rendering.mesh> #include <helios.engine.rendering.renderTarget.RenderTargetEntity> #include <helios.engine.rendering.common.components> #include <helios.engine.rendering.renderTarget> #include <helios.engine.spatial.components> #include <helios.engine.core.components> #include <helios.engine.scene.types> #include <helios.engine.rendering.shader>

Namespaces Index

namespacehelios
namespaceopengl
namespacerendering
namespacecomponents
namespaceshader
namespacecomponents
namespacetypes
namespacetypes
namespacecomponents
namespacecomponents
namespacerenderTarget
namespacetypes
namespaceviewport
namespacetypes
namespacecomponents
namespacetypes
namespaceworld
namespacelog

Classes Index

classOpenGLBackend

Applies render-pass state and executes OpenGL draw calls. More...

structViewProjection

Per-viewport camera matrices used for a render pass. More...

Macro Definitions Index

#defineHELIOS_LOG_SCOPE   "helios::opengl"

Description

OpenGL backend for render-pass execution and indexed draw submission.

Macro Definitions

HELIOS_LOG_SCOPE

#define HELIOS_LOG_SCOPE   "helios::opengl"

Definition at line 75 of file OpenGLBackend.ixx.

75#define HELIOS_LOG_SCOPE "helios::opengl"

File Listing

The file content with the documentation metadata removed is:

1
5module;
6
7#include <glad/gl.h>
8#include <GLFW/glfw3.h>
9#include <memory>
10#include <cassert>
11#include <utility>
12#include <type_traits>
13#include <span>
15#include <optional>
16
17export module helios.opengl.OpenGLBackend;
18
19import helios.math;
20
21import helios.engine.util.log;
22
23import helios.engine.rendering.viewport.ViewportEntity;
24
25import helios.engine.rendering.renderTarget.RenderTargetEntity;
26import helios.engine.rendering.common.types;
27import helios.engine.rendering.common.components;
28
29import helios.engine.spatial.components;
30
31import helios.engine.core.components;
32
33import helios.opengl.OpenGLUniformWriter;
34import helios.opengl.components;
35import helios.opengl.types;
36
37
38import helios.engine.rendering.mesh;
39import helios.engine.rendering.shader;
40import helios.engine.rendering.material;
41import helios.engine.rendering.renderTarget;
42import helios.engine.rendering.viewport;
43import helios.engine.util.Colors;
44
45import helios.engine.scene.components;
46import helios.engine.scene.types;
47
48import helios.engine.runtime.world.EngineWorld;
49
50import helios.opengl.types;
51
52using namespace helios::engine::core::components;
53using namespace helios::engine::rendering;
54using namespace helios::engine::rendering::mesh::components;
55using namespace helios::engine::rendering::shader;
56using namespace helios::engine::rendering::shader::types;
57using namespace helios::opengl;
58using namespace helios::opengl::components;
59using namespace helios::opengl::types;
60using namespace helios::engine::spatial::components;
61using namespace helios::engine::rendering::material::types;
62using namespace helios::engine::rendering::common::types;
63using namespace helios::engine::rendering::common::components;
64using namespace helios::engine::rendering::shader::components;
65using namespace helios::engine::rendering::mesh::types;
66using namespace helios::engine::rendering::renderTarget;
67using namespace helios::engine::rendering::renderTarget::types;
68using namespace helios::engine::rendering::viewport;
69using namespace helios::engine::rendering::viewport::types;
70using namespace helios::engine::scene::components;
71using namespace helios::engine::scene::types;
72using namespace helios::engine::runtime::world;
73using namespace helios::engine::util::log;
74
75#define HELIOS_LOG_SCOPE "helios::opengl"
76export namespace helios::opengl {
77
78
86 private:
87
91 bool isInitialized_ = false;
92
96 inline static const helios::engine::util::log::Logger& logger_ = helios::engine::util::log::LogManager::loggerForScope(
98 );
99
103 OpenGLMeshComponent<MeshHandle>* currentOpenGLMesh_ = nullptr;
104
108 UniformValueBag<UniformScope::Pass> passUniformValueBag_{};
109
113 UniformValueBag<UniformScope::Draw> drawUniformValueBag_{};
114
118 UniformValueBag<UniformScope::Material> materialUniformValueBag_{};
119
123 RenderTargetHandle currentRenderTargetHandle_{};
124
128 ShaderHandle currentShaderHandle_{};
129
130
134 EngineWorld& engineWorld_;
135
139 struct ViewProjection {
140 helios::math::mat4f viewMatrix;
141 helios::math::mat4f projectionMatrix;
142 };
143
150 [[nodiscard]] std::optional<ViewProjection> viewProjection(const ViewportEntity& viewportEntity) const noexcept {
151
152 auto* cbc = viewportEntity.get<CameraBindingComponent<ViewportHandle>>();
153 if (!cbc) {
154 logger_.error("Expected CameraBindingComponent on ViewportEntity, but couldn't find any.");
155 return std::nullopt;
156 }
157 auto camera = engineWorld_.find(cbc->targetHandle());
158 if (!camera) {
159 logger_.error("Expected CameraEntity, but couldn't find any.");
160 return std::nullopt;
161 }
162 using CameraHandleType = std::remove_cvref_t<decltype(cbc->targetHandle())>;
163 auto* vm = camera->get<ViewMatrixComponent<CameraHandleType>>();
164 if (!vm) {
165 logger_.error("Expected ViewMatrixComponent, but couldn't find any.");
166 return std::nullopt;
167 }
168
169 auto* pm = camera->get<ProjectionMatrixComponent<CameraHandleType>>();
170 if (!pm) {
171 logger_.error("Expected ProjectionMatrixComponent, but couldn't find any.");
172 return std::nullopt;
173 }
174
175 return ViewProjection{
176 vm->value(), pm->value()
177 };
178
179 }
180
193 template<typename TUniformScope>
194 void writeUniformValues(ShaderEntity shaderEntity, UniformValueBag<TUniformScope>& uniformValueBag) noexcept {
195
197
198 if (!ulc) {
199 logger_.error("OpenGLUniformWriteOperationsComponent<{0}> expected, but not found", typeid(TUniformScope).name());
200 assert(false && "OpenGLUniformWriteOperationsComponent not found");
201 return;
202 }
203
204 OpenGLUniformWriter::write(ulc->operations, uniformValueBag);
205 }
206
207
215 template<typename THandle, typename TEntity>
216 void clearColor(TEntity& entity) noexcept {
217
218 auto* colorComp = entity->template get<ColorComponent<THandle>>();
219 auto* clearComp = entity->template get<ClearComponent<THandle>>();
220
221 if (colorComp) {
222 const auto clearColor = colorComp->value();
223 glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
224 }
225
226 if (clearComp) {
227 const auto clearFlags = std::to_underlying(clearComp->flags);
228 const auto clearMask = ((clearFlags & std::to_underlying(ClearFlags::Color)) ? GL_COLOR_BUFFER_BIT : 0) |
229 ((clearFlags & std::to_underlying(ClearFlags::Depth)) ? GL_DEPTH_BUFFER_BIT : 0) |
230 ((clearFlags & std::to_underlying(ClearFlags::Stencil)) ? GL_STENCIL_BUFFER_BIT : 0);
231
232 if (clearMask != 0) {
233 glClear(clearMask);
234 }
235 }
236 }
237
238
239 public:
240
241
242
248 explicit OpenGLBackend(EngineWorld& engineWorld) : engineWorld_(engineWorld) {}
249
250
259 void beginRenderTargetBatch(const RenderTargetHandle renderTargetHandle) noexcept {
260
261 auto renderTargetEntity = engineWorld_.find<RenderTargetHandle>(renderTargetHandle);
262
263 #ifdef HELIOS_DEBUG
264 if (!renderTargetEntity) {
265 logger_.error("Missing RenderTargetEntity for handle {0}.", renderTargetHandle.entityId);
266 assert(renderTargetEntity && "Missing RenderTargetEntity for handle.");
267 }
268 #endif
269
270 currentRenderTargetHandle_ = renderTargetHandle;
271
272 const auto renderTargetId = renderTargetEntity->get<OpenGLRenderTargetIdComponent<RenderTargetHandle>>()->value();
273
274 glBindFramebuffer(GL_FRAMEBUFFER, renderTargetId);
275
276 #ifdef HELIOS_DEBUG
277 const auto isValidRenderTarget = renderTargetId == 0 ||
278 (glIsFramebuffer(renderTargetId) == GL_TRUE && glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
279 if (!isValidRenderTarget) {
280 logger_.error("RenderTargetEntity with EntityId {0} undefined.", renderTargetId);
281 assert(isValidRenderTarget && "RenderTargetEntity EntityId does not seem to be a valid id.");
282 }
283 #endif
284
285 auto renderTargetSize = renderTargetEntity->get<Size2DComponent<RenderTargetHandle>>()->value();
286
287 glViewport(0, 0,
288 static_cast<int>(renderTargetSize[0]),
289 static_cast<int>(renderTargetSize[1])
290 );
291
292 clearColor<RenderTargetHandle>(renderTargetEntity);
293
294 // this is equally important for the GlpyhTextRenderer
295 // enable blending since the font's fragment shader uses the alpha channel
296 glEnable(GL_BLEND);
297 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
298 }
299
307 void endRenderTargetBatch(const RenderTargetHandle renderTargetHandle) noexcept {
308
309 currentRenderTargetHandle_ = RenderTargetHandle{};
310 passUniformValueBag_.clearValues();
311 }
312
321 void beginViewportBatch(const ViewportHandle viewportHandle) noexcept {
322
323 auto viewport = engineWorld_.find<ViewportHandle>(viewportHandle);
324 auto renderTargetEntity = engineWorld_.find<RenderTargetHandle>(currentRenderTargetHandle_);
325
326 #ifdef HELIOS_DEBUG
327 if (!renderTargetEntity) {
328 logger_.error("Missing RenderTargetEntity for handle {0}.", renderTargetEntity->handle().entityId);
329 assert(renderTargetEntity && "Missing RenderTargetEntity for handle.");
330 }
331 if (!viewport) {
332 logger_.error("Missing Viewport for handle {0}.", viewportHandle.entityId);
333 assert(viewport && "Missing Viewport for handle.");
334 }
335 #endif
336
337 auto vp = viewProjection(*viewport);
338 if (!vp) {
339 logger_.warn("Could not determine View/Projection-matrices for RenderPass");
340 passUniformValueBag_.set<ProjectionMatrixUniform>(helios::math::mat4f{1.0f});
341 passUniformValueBag_.set<ViewMatrixUniform>(helios::math::mat4f{1.0f});
342 } else {
343 passUniformValueBag_.set<ProjectionMatrixUniform>(vp->projectionMatrix);
344 passUniformValueBag_.set<ViewMatrixUniform>(vp->viewMatrix);
345 }
346
347 auto viewportBounds = viewport->get<RectComponent<ViewportHandle>>()->value();
348 auto renderTargetSize = renderTargetEntity->get<Size2DComponent<RenderTargetHandle>>()->value();
349
350 const auto x = static_cast<int>(renderTargetSize[0] * viewportBounds[0]);
351 const auto y = static_cast<int>(renderTargetSize[1] * viewportBounds[1]);
352 const auto width = static_cast<int>(renderTargetSize[0] * viewportBounds[2]);
353 const auto height = static_cast<int>(renderTargetSize[1] * viewportBounds[3]);
354
355
356 glViewport(x, y, width, height);
357 glScissor(x, y, width, height);
358 glEnable(GL_SCISSOR_TEST);
359
360 clearColor<ViewportHandle>(viewport);
361 }
362
368 void endViewportBatch(const ViewportHandle viewportHandle) noexcept {
369 glDisable(GL_SCISSOR_TEST);
370 }
371
379 void beginShaderBatch(ShaderHandle shaderHandle) noexcept {
380
381 auto shaderEntity = engineWorld_.find(shaderHandle);
382 if (!shaderEntity) {
383 logger_.error("ShaderEntity expected, but not found");
384 assert(false && "ShaderEntity not found");
385 return;
386 }
387
388 currentShaderHandle_ = shaderHandle;
389
390 auto* openglShader = shaderEntity->template get<OpenGLShaderComponent<ShaderHandle>>();
391 if (!openglShader) {
392 logger_.error("OpenGLShader expected, but not found");
393 assert(false && "OpenGLShader not found");
394 return;
395 }
396
397 glUseProgram(openglShader->programId);
398 writeUniformValues<UniformScope::Pass>(*shaderEntity, passUniformValueBag_);
399 }
400
406 void endShaderBatch(ShaderHandle handle) noexcept {
407 currentShaderHandle_ = ShaderHandle{};
408 }
409
417 void beginMaterialBatch(MaterialHandle materialHandle) noexcept {
418 auto materialEntity = engineWorld_.find(materialHandle);
419 if (!materialEntity) {
420 logger_.error("MaterialEntity expected, but not found");
421 assert(false && "MaterialEntity not found");
422 return;
423 }
424
425 auto* colorComponent = materialEntity->template get<ColorComponent<MaterialHandle>>();
426 if (colorComponent) {
427 materialUniformValueBag_.set<MaterialBaseColorUniform>(colorComponent->value());
428 const auto shaderEntity = engineWorld_.find(currentShaderHandle_);
429 assert(shaderEntity && "ShaderEntity expected, but not found");
430 writeUniformValues<UniformScope::Material>(*shaderEntity, materialUniformValueBag_);
431 }
432
433 }
434
440 void endMaterialBatch(MaterialHandle handle) noexcept {
441 materialUniformValueBag_.clearValues();
442 }
443
451 void beginMeshBatch(MeshHandle meshHandle) noexcept {
452
453 auto meshEntity = engineWorld_.find(meshHandle);
454 if (!meshEntity) {
455 logger_.error("MeshEntity expected, but not found");
456 assert(false && "MeshEntity not found");
457 return;
458 }
459
460 auto* openglMesh = meshEntity->template get<OpenGLMeshComponent<MeshHandle>>();
461 if (!openglMesh) {
462 logger_.error("OpenGLMesh expected, but not found");
463 assert(false && "OpenGLMesh not found");
464 return;
465 } else {
466 currentOpenGLMesh_ = openglMesh;
467 glBindVertexArray(openglMesh->vao);
468 }
469 }
470
476 void endMeshBatch(MeshHandle handle) noexcept {
477 currentOpenGLMesh_ = nullptr;
478 glBindVertexArray(0);
479 }
480
481
491 template<typename THandle>
492 void renderBatch(std::span<const SceneMemberRenderContext<THandle>> sceneMemberRenderContexts) noexcept {
493
494 if (!currentOpenGLMesh_) {
495 logger_.error("OpenGLMesh expected, but not available");
496 return;
497 }
498 if (!currentShaderHandle_.isValid()) {
499 logger_.error("Expected valid currentShaderHandle_, but found {0}.", currentShaderHandle_.entityId);
500 return;
501 }
502
503 const auto shaderEntity = engineWorld_.find(currentShaderHandle_);
504
505 assert(shaderEntity && "ShaderEntity expected, but not found");
506 assert(currentOpenGLMesh_ && "Current OpenGL mesh expected, but not found");
507
508 for (auto& renderContext : sceneMemberRenderContexts) {
509
510 drawUniformValueBag_.set<ModelMatrixUniform>(renderContext.worldMatrix);
511 writeUniformValues<UniformScope::Draw>(*shaderEntity, drawUniformValueBag_);
512 glDrawElements(
513 currentOpenGLMesh_->primitiveType,
514 currentOpenGLMesh_->indexCount,
515 GL_UNSIGNED_INT,
516 nullptr
517 );
518 }
519
520 drawUniformValueBag_.clearValues();
521 }
522
532 template<typename THandle>
533 void renderBatch(std::span<const InstanceData<THandle>> instanceData) noexcept {
534
535 if (!currentOpenGLMesh_) {
536 logger_.error("OpenGLMesh expected, but not available");
537 return;
538 }
539
540 assert(currentOpenGLMesh_ && "Current OpenGL mesh expected, but not found");
541
542 const auto instanceSize = instanceData.size();
543
544 assert(instanceSize <= 1000000 && "Instance data size seems unreasonably large.");
545
546 if (instanceSize <= 0) {
547 return;
548 }
549
550 assert(currentOpenGLMesh_->instanceVbo && "Using instancing without configured instanceVbo");
551 glBindBuffer(GL_ARRAY_BUFFER, currentOpenGLMesh_->instanceVbo);
552
553 glBufferData(
554 GL_ARRAY_BUFFER,
555 instanceSize * sizeof(InstanceData<THandle>),
556 instanceData.data(),
557 GL_DYNAMIC_DRAW);
558
559
560 glDrawElementsInstanced(
561 currentOpenGLMesh_->primitiveType,
562 currentOpenGLMesh_->indexCount,
563 GL_UNSIGNED_INT,
564 nullptr,
565 instanceSize
566 );
567
568 glBindBuffer(GL_ARRAY_BUFFER, 0);
569 }
570
577 void provideWindowHints() noexcept {
578
579 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
580 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
581 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
582
583 }
584
592 [[nodiscard]] bool init() noexcept {
593
594 assert(!isInitialized_ && "Backend already initialized");
595
596 const GLADloadfunc procAddressLoader = glfwGetProcAddress;
597 const int gl_ver = gladLoadGL(procAddressLoader);
598
599 if (gl_ver == 0) {
600 logger_.error("Failed to load OpenGL");
601 assert(false && "Failed to load OpenGL");
602 return false;
603 }
604
605 logger_.info("OpenGL {0}.{1} loaded", GLAD_VERSION_MAJOR(gl_ver), GLAD_VERSION_MINOR(gl_ver));
606
607 isInitialized_ = true;
608 return true;
609
610 }
611
615 [[nodiscard]] bool isInitialized() const noexcept {
616 return isInitialized_;
617 }
618
619 };
620} // namespace helios::opengl

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.9.8.