Skip to content

Rendering Abstraction Layer #97

Description

@juanchuletas

Phase 1

Description

Introduce backend-agnostic interfaces so the engine routes all GPU calls through an abstraction instead of calling OpenGL directly. No Vulkan code is written in this phase — the goal is to decouple the engine from OpenGL so that a second backend can be plugged in later without touching core engine logic.

Currently, raw gl* calls are spread across VertexGL/, Mesh/, Textures/, Shaders/, GT/, Imgui_Setup/, and SceneManager/. After this phase, all of those go through a set of interfaces, and the existing OpenGL code lives behind an OpenGL implementation of those interfaces.

Motivation

Renders/display_graphics.hpp already defines a Backend enum with OpenGL, Vulkan, and Metal — but nothing dispatches on it. This phase makes that enum meaningful.

Tasks

  • 1. RenderDevice interface (RenderBackend/render_device.hpp)

    • Abstract: init/shutdown, create buffers, create textures, create pipelines, begin/end frame
    • Factory method that returns the correct implementation based on DisplayGraphics::GetBackend()
  • 2. GPUBuffer interface (RenderBackend/gpu_buffer.hpp)

    • Replaces VertexArrayObject, VertexBuffer, VertexIndex from VertexGL/
    • Operations: create(type, size, data), bind(), unbind(), updateData()
    • Buffer types: Vertex, Index, Uniform
  • 3. GPUTexture interface (RenderBackend/gpu_texture.hpp)

    • Replaces the GL internals of Textures/textures.hpp
    • Operations: create(width, height, format, data), bind(slot), destroy()
    • Supports 2D and CubeMap targets
  • 4. Pipeline interface (RenderBackend/pipeline.hpp)

    • Replaces Shaders/shader.hpp concept (Vulkan binds entire pipelines, not individual shaders)
    • Operations: create(vertexSrc, fragmentSrc), bind(), setUniform*()
    • Encapsulates vertex input layout, blend state, depth state
  • 5. RenderPass abstraction (RenderBackend/render_pass.hpp)

    • Wraps the concept of "clear → draw → present" that OpenGL does implicitly
    • Operations: begin(clearColor), end(), submit()
    • In the OpenGL implementation this simply maps to glClear / glfwSwapBuffers
  • 6. OpenGL backend implementation (RenderBackend/opengl/)

    • GLDevice, GLBuffer, GLTexture, GLPipeline, GLRenderPass
    • Move existing GL code from VertexGL/, Shaders/, Textures/ into these classes
    • The old classes in VertexGL/ become thin wrappers or are replaced entirely
  • 7. Refactor GraphicsTool::initGL()initBackend()

    • Dispatch to RenderDevice::create() based on DisplayGraphics::GetBackend()
    • Remove hardcoded glfwWindowHint(OPENGL_PROFILE, ...) and glewInit() from the generic path
    • GLFW window hints become backend-specific (OpenGL needs OPENGL_PROFILE; Vulkan needs GLFW_CLIENT_API, GLFW_NO_API)
  • 8. Refactor Mesh to use GPUBuffer

    • Replace VertexArrayObject m_vao, VertexBuffer m_vb, VertexIndex m_vi with interface pointers
    • Remove raw glEnableVertexAttribArray / glVertexAttribPointer calls from mesh.cpp — vertex layout goes into Pipeline
    • Mesh::draw() calls pipeline->bind() + renderDevice->drawIndexed() instead of glDrawElements
  • 9. Refactor SceneManager::renderScene()

    • Replace shader.Bind() + shader.setUniformMat4fv() calls with pipeline->bind() + pipeline->setUniform*()
    • Wrap the render loop body in renderPass->begin() / renderPass->end()
  • 10. Refactor Imgui_Setup for backend awareness

    • Guard ImGui_ImplOpenGL3_* calls behind if (GetBackend() == OpenGL)
    • Prepare the structure so a future Vulkan ImGui backend can be added in the same file

Suggested file structure after this phase

RenderBackend/
├── render_device.hpp
├── gpu_buffer.hpp
├── gpu_texture.hpp
├── pipeline.hpp
├── render_pass.hpp
└── opengl/
    ├── gl_device.cpp / .hpp
    ├── gl_buffer.cpp / .hpp
    ├── gl_texture.cpp / .hpp
    ├── gl_pipeline.cpp / .hpp
    └── gl_render_pass.cpp / .hpp

Affected files

Current file | Change -- | -- VertexGL/vertexArrayObjects.hpp/.cpp | Internals move to GLBuffer; may become a thin wrapper or be removed VertexGL/vertexBuffers.hpp/.cpp | Same VertexGL/vertexIndices.hpp/.cpp | Same Shaders/shader.hpp/.cpp | Internals move to GLPipeline Textures/textures.hpp/.cpp | GL internals move to GLTexture GT/graphicsTool.hpp/.cpp | initGL() → initBackend(), render loop uses RenderPass Mesh/mesh.hpp/.cpp | Uses GPUBuffer + Pipeline instead of raw GL SceneManager/scene_manager.cpp | Uses Pipeline interface for uniforms Imgui_Setup/imgui_setup.cpp | Backend-guarded init/render/shutdown Renders/display_graphics.hpp | Add factory for RenderDevice CMakeLists.txt | Add RenderBackend/ sources

Acceptance criteria

  • Engine compiles and runs identically to current behavior using the OpenGL backend
  • No raw gl* calls exist outside of RenderBackend/opengl/
  • DisplayGraphics::GetBackend() is checked at initialization to select the backend
  • Adding a new backend only requires implementing the interfaces — zero changes to Mesh, Model, SceneManager, or application code

Notes

  • This phase intentionally does not add any Vulkan code — it only prepares the engine for it
  • The CRTP pattern in GraphicsTool can coexist with runtime dispatch for backends; CRTP handles application-level polymorphism (FunGT derives from GraphicsTool<FunGT>), while the backend uses virtual interfaces
  • SYCL/OpenCL compute kernels in Physics/GPU/ are out of scope for this phase

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions