Skip to content

Commit

Permalink
Zephyr: Renderer: split OpenGL backend into multiple files
Browse files Browse the repository at this point in the history
  • Loading branch information
fleroviux committed May 4, 2024
1 parent 234dc72 commit d9e2ceb
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 223 deletions.
3 changes: 3 additions & 0 deletions zephyr/renderer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@

set(SOURCES
src/backend/opengl/render_backend.cpp
src/backend/opengl/render_geometry.cpp
src/backend/vulkan/render_backend.cpp
src/engine/geometry_cache.cpp
src/vulkan/vulkan_instance.cpp
src/render_engine.cpp
)

set(HEADERS
src/backend/opengl/render_geometry.hpp
src/backend/opengl/render_backend.hpp
src/backend/vulkan/shader/triangle.frag.h
src/backend/vulkan/shader/triangle.vert.h
)
Expand Down
329 changes: 106 additions & 223 deletions zephyr/renderer/src/backend/opengl/render_backend.cpp
Original file line number Diff line number Diff line change
@@ -1,265 +1,148 @@

#include <zephyr/renderer/backend/render_backend_ogl.hpp>
#include <zephyr/panic.hpp>
#include <GL/glew.h>
#include <GL/gl.h>
#include <SDL_opengl.h>

#include <array>
#include <optional>
#include <vector>
#include "render_backend.hpp"

namespace zephyr {

class OpenGLRenderGeometry final : public RenderGeometry {
public:
static OpenGLRenderGeometry* Build(RenderGeometryLayout layout, size_t number_of_vertices, size_t number_of_indices) {
GLuint gl_vao;
glCreateVertexArrays(1u, &gl_vao);

std::optional<GLuint> maybe_gl_ibo{};

if(number_of_indices > 0) {
// @todo: use GL_STATIC_DRAW when possible.
GLuint gl_ibo;
glCreateBuffers(1u, &gl_ibo);
glNamedBufferData(gl_ibo, (GLsizeiptr)(sizeof(u32) * number_of_indices), nullptr, GL_DYNAMIC_DRAW);
glVertexArrayElementBuffer(gl_vao, gl_ibo);
maybe_gl_ibo = gl_ibo;
}

size_t vbo_stride = 0u;
std::array<size_t, 32> vbo_attribute_offsets{};

const auto PackAttribute = [&](RenderGeometryAttribute attribute, int number_of_components) {
if(layout.HasAttribute(attribute)) {
glEnableVertexArrayAttrib(gl_vao, (int)attribute);
glVertexArrayAttribFormat(gl_vao, (int)attribute, number_of_components, GL_FLOAT, GL_FALSE, vbo_stride);
glVertexArrayAttribBinding(gl_vao, (int)attribute, 0u);

vbo_attribute_offsets[(int)attribute] = vbo_stride;
vbo_stride += sizeof(f32) * number_of_components;
}
};

PackAttribute(RenderGeometryAttribute::Position, 3);
PackAttribute(RenderGeometryAttribute::Normal, 3);
PackAttribute(RenderGeometryAttribute::UV, 2);
PackAttribute(RenderGeometryAttribute::Color, 4);

// @todo: use GL_STATIC_DRAW when possible.
GLuint gl_vbo;
glCreateBuffers(1u, &gl_vbo);
glNamedBufferData(gl_vbo, (GLsizeiptr)(vbo_stride * number_of_vertices), nullptr, GL_DYNAMIC_DRAW);
glVertexArrayVertexBuffer(gl_vao, 0u, gl_vbo, 0u, (GLsizei)vbo_stride);

OpenGLRenderGeometry* render_geometry = new OpenGLRenderGeometry{};
render_geometry->m_gl_vao = gl_vao;
render_geometry->m_gl_ibo = maybe_gl_ibo;
render_geometry->m_gl_vbo = gl_vbo;
render_geometry->m_vbo_stride = vbo_stride;
render_geometry->m_vbo_attribute_offsets = vbo_attribute_offsets;
render_geometry->m_number_of_indices = number_of_indices;
render_geometry->m_number_of_vertices = number_of_vertices;
return render_geometry;
}

[[nodiscard]] size_t GetNumberOfVertices() const override {
return m_number_of_vertices;
}

[[nodiscard]] size_t GetNumberOfIndices() const override {
return m_number_of_indices;
}

void UpdateIndices(std::span<const u8> data) {
// @todo: validation
glNamedBufferSubData(m_gl_ibo.value(), 0u, (GLsizeiptr)data.size_bytes(), data.data());
}

void UpdateVertices(std::span<const u8> data) {
//@todo: validation
glNamedBufferSubData(m_gl_vbo, 0u, (GLsizeiptr)data.size_bytes(), data.data());
}

void Draw() {
glBindVertexArray(m_gl_vao);
OpenGLRenderBackend::OpenGLRenderBackend(SDL_Window* sdl2_window) : m_window{sdl2_window} {
}

if(m_gl_ibo.has_value()) {
glDrawElements(GL_TRIANGLES, (GLsizei)m_number_of_indices, GL_UNSIGNED_INT, nullptr);
} else {
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_number_of_vertices);
}
}
void OpenGLRenderBackend::InitializeContext() {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
m_gl_context = SDL_GL_CreateContext(m_window);
if(m_gl_context == nullptr) {
ZEPHYR_PANIC("Failed to create OpenGL context: {}", SDL_GetError());
}
glewInit();

private:
static_assert((int)RenderGeometryAttribute::Count <= 32);
CreateShaderProgram();
glGenVertexArrays(1u, &m_gl_vao);

OpenGLRenderGeometry() = default;
glCreateBuffers(1u, &m_gl_ubo);
glNamedBufferData(m_gl_ubo, sizeof(Matrix4) * 2, nullptr, GL_DYNAMIC_DRAW);

GLuint m_gl_vao{};
std::optional<GLuint> m_gl_ibo{};
GLuint m_gl_vbo{};
size_t m_vbo_stride{};
std::array<size_t, 32> m_vbo_attribute_offsets{};
size_t m_number_of_indices{};
size_t m_number_of_vertices{};
};
glEnable(GL_DEPTH_TEST);
}

class OpenGLRenderBackend final : public RenderBackend {
public:
explicit OpenGLRenderBackend(SDL_Window* sdl2_window) : m_window{sdl2_window} {
}
void OpenGLRenderBackend::DestroyContext() {
glDeleteBuffers(1u, &m_gl_ubo);
glDeleteVertexArrays(1u, &m_gl_vao);
glDeleteProgram(m_gl_shader_program);

void InitializeContext() override {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
m_gl_context = SDL_GL_CreateContext(m_window);
if(m_gl_context == nullptr) {
ZEPHYR_PANIC("Failed to create OpenGL context: {}", SDL_GetError());
}
glewInit();
SDL_GL_DeleteContext(m_gl_context);
}

CreateShaderProgram();
glGenVertexArrays(1u, &m_gl_vao);
RenderGeometry* OpenGLRenderBackend::CreateRenderGeometry(RenderGeometryLayout layout, size_t number_of_vertices, size_t number_of_indices) {
return OpenGLRenderGeometry::Build(layout, number_of_vertices, number_of_indices);
}

glCreateBuffers(1u, &m_gl_ubo);
glNamedBufferData(m_gl_ubo, sizeof(Matrix4) * 2, nullptr, GL_DYNAMIC_DRAW);
void OpenGLRenderBackend::UpdateRenderGeometryIndices(RenderGeometry* render_geometry, std::span<const u8> data) {
dynamic_cast<OpenGLRenderGeometry*>(render_geometry)->UpdateIndices(data);
}

glEnable(GL_DEPTH_TEST);
}
void OpenGLRenderBackend::UpdateRenderGeometryVertices(RenderGeometry* render_geometry, std::span<const u8> data) {
dynamic_cast<OpenGLRenderGeometry*>(render_geometry)->UpdateVertices(data);
}

void DestroyContext() override {
glDeleteBuffers(1u, &m_gl_ubo);
glDeleteVertexArrays(1u, &m_gl_vao);
glDeleteProgram(m_gl_shader_program);
void OpenGLRenderBackend::DestroyRenderGeometry(RenderGeometry* geometry) {
delete geometry;
}

SDL_GL_DeleteContext(m_gl_context);
}
void OpenGLRenderBackend::Render(const Matrix4& view_projection, std::span<const RenderObject> render_objects) {
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

RenderGeometry* CreateRenderGeometry(RenderGeometryLayout layout, size_t number_of_vertices, size_t number_of_indices) override {
return OpenGLRenderGeometry::Build(layout, number_of_vertices, number_of_indices);
}
glUseProgram(m_gl_shader_program);

void UpdateRenderGeometryIndices(RenderGeometry* render_geometry, std::span<const u8> data) override {
dynamic_cast<OpenGLRenderGeometry*>(render_geometry)->UpdateIndices(data);
}
glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_gl_ubo);
glNamedBufferSubData(m_gl_ubo, 0, sizeof(Matrix4), &view_projection);

void UpdateRenderGeometryVertices(RenderGeometry* render_geometry, std::span<const u8> data) override {
dynamic_cast<OpenGLRenderGeometry*>(render_geometry)->UpdateVertices(data);
}
for(const RenderObject& render_object : render_objects) {
glNamedBufferSubData(m_gl_ubo, sizeof(Matrix4), sizeof(Matrix4), &render_object.local_to_world);
dynamic_cast<OpenGLRenderGeometry*>(render_object.render_geometry)->Draw();
}
}

void DestroyRenderGeometry(RenderGeometry* geometry) override {
delete geometry;
}
void OpenGLRenderBackend::SwapBuffers() {
SDL_GL_SwapWindow(m_window);
}

void Render(const Matrix4& view_projection, std::span<const RenderObject> render_objects) override {
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
void OpenGLRenderBackend::CreateShaderProgram() {
GLuint vert_shader = CreateShader(R"(
#version 450 core
glUseProgram(m_gl_shader_program);
layout(binding = 0, std140) uniform Transform {
mat4 projection;
mat4 local_to_world;
} u_transform;
glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_gl_ubo);
glNamedBufferSubData(m_gl_ubo, 0, sizeof(Matrix4), &view_projection);
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_normal;
layout(location = 2) in vec2 a_uv;
layout(location = 3) in vec3 a_color;
for(const RenderObject& render_object : render_objects) {
glNamedBufferSubData(m_gl_ubo, sizeof(Matrix4), sizeof(Matrix4), &render_object.local_to_world);
dynamic_cast<OpenGLRenderGeometry*>(render_object.render_geometry)->Draw();
}
}
out vec3 v_normal;
out vec3 v_color;
void SwapBuffers() override {
SDL_GL_SwapWindow(m_window);
void main() {
v_normal = a_normal;
v_color = a_color;
gl_Position = u_transform.projection * u_transform.local_to_world * vec4(a_position, 1.0);
}
)", GL_VERTEX_SHADER);

private:
void CreateShaderProgram() {
GLuint vert_shader = CreateShader(R"(
#version 450 core
layout(binding = 0, std140) uniform Transform {
mat4 projection;
mat4 local_to_world;
} u_transform;
GLuint frag_shader = CreateShader(R"(
#version 450 core
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_normal;
layout(location = 2) in vec2 a_uv;
layout(location = 3) in vec3 a_color;
layout(location = 0) out vec4 f_frag_color;
out vec3 v_normal;
out vec3 v_color;
in vec3 v_normal;
in vec3 v_color;
void main() {
v_normal = a_normal;
v_color = a_color;
gl_Position = u_transform.projection * u_transform.local_to_world * vec4(a_position, 1.0);
}
)", GL_VERTEX_SHADER);

GLuint frag_shader = CreateShader(R"(
#version 450 core
layout(location = 0) out vec4 f_frag_color;
in vec3 v_normal;
in vec3 v_color;
void main() {
f_frag_color = vec4(v_normal * 0.5 + 0.5, 1.0);
}
)", GL_FRAGMENT_SHADER);

m_gl_shader_program = CreateProgram({{vert_shader, frag_shader}});
glDeleteShader(vert_shader);
glDeleteShader(frag_shader);
void main() {
f_frag_color = vec4(v_normal * 0.5 + 0.5, 1.0);
}
)", GL_FRAGMENT_SHADER);

static GLuint CreateShader(const char* glsl_code, GLenum type) {
GLuint gl_shader = glCreateShader(type);

const char* glsl_code_array[] = {glsl_code};
glShaderSource(gl_shader, 1u, glsl_code_array, nullptr);
glCompileShader(gl_shader);
m_gl_shader_program = CreateProgram({{vert_shader, frag_shader}});
glDeleteShader(vert_shader);
glDeleteShader(frag_shader);
}

GLint compile_succeeded;
glGetShaderiv(gl_shader, GL_COMPILE_STATUS, &compile_succeeded);
if(compile_succeeded == GL_FALSE) {
// TODO(fleroviux): let the caller decide how to deal with the error.
GLint info_log_length;
glGetShaderiv(gl_shader, GL_INFO_LOG_LENGTH, &info_log_length);
GLuint OpenGLRenderBackend::CreateShader(const char* glsl_code, GLenum type) {
GLuint gl_shader = glCreateShader(type);

GLchar* info_log = new GLchar[info_log_length];
glGetShaderInfoLog(gl_shader, info_log_length, &info_log_length, info_log);
ZEPHYR_PANIC("OpenGL: failed to compile GLSL shader:\n{}", info_log);
delete[] info_log;
}
const char* glsl_code_array[] = {glsl_code};
glShaderSource(gl_shader, 1u, glsl_code_array, nullptr);
glCompileShader(gl_shader);

return gl_shader;
}
GLint compile_succeeded;
glGetShaderiv(gl_shader, GL_COMPILE_STATUS, &compile_succeeded);
if(compile_succeeded == GL_FALSE) {
// TODO(fleroviux): let the caller decide how to deal with the error.
GLint info_log_length;
glGetShaderiv(gl_shader, GL_INFO_LOG_LENGTH, &info_log_length);

static GLuint CreateProgram(std::span<const GLuint> shaders) {
GLuint gl_program = glCreateProgram();
GLchar* info_log = new GLchar[info_log_length];
glGetShaderInfoLog(gl_shader, info_log_length, &info_log_length, info_log);
ZEPHYR_PANIC("OpenGL: failed to compile GLSL shader:\n{}", info_log);
delete[] info_log;
}

for(auto shader_object : shaders) {
glAttachShader(gl_program, shader_object);
}
glLinkProgram(gl_program);
return gl_shader;
}

// TODO: handle program link error?
return gl_program;
}
GLuint OpenGLRenderBackend::CreateProgram(std::span<const GLuint> shaders) {
GLuint gl_program = glCreateProgram();

SDL_Window* m_window;
SDL_GLContext m_gl_context{};
GLuint m_gl_shader_program{};
GLuint m_gl_vao{};
GLuint m_gl_ubo{};
};
for(auto shader_object : shaders) {
glAttachShader(gl_program, shader_object);
}
glLinkProgram(gl_program);

std::unique_ptr<RenderBackend> CreateOpenGLRenderBackendForSDL2(SDL_Window* sdl2_window) {
return std::make_unique<OpenGLRenderBackend>(sdl2_window);
// TODO: handle program link error?
return gl_program;
}

} // namespace zephyr
Loading

0 comments on commit d9e2ceb

Please sign in to comment.