aboutsummaryrefslogtreecommitdiff
path: root/src/platform/gui/sdl/OpenGLRenderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/gui/sdl/OpenGLRenderer.cpp')
-rw-r--r--src/platform/gui/sdl/OpenGLRenderer.cpp226
1 files changed, 222 insertions, 4 deletions
diff --git a/src/platform/gui/sdl/OpenGLRenderer.cpp b/src/platform/gui/sdl/OpenGLRenderer.cpp
index 7f732cb3..7e8fdf33 100644
--- a/src/platform/gui/sdl/OpenGLRenderer.cpp
+++ b/src/platform/gui/sdl/OpenGLRenderer.cpp
@@ -1,9 +1,46 @@
#include "cru/platform/gui/sdl/OpenGLRenderer.h"
+#include "cru/base/Base.h"
#include "cru/base/log/Logger.h"
+#include "cru/platform/graphics/cairo/CairoPainter.h"
+#include "cru/platform/gui/sdl/Base.h"
#include "cru/platform/gui/sdl/Window.h"
+#include <SDL3/SDL_video.h>
+#include <cairo.h>
+
namespace cru::platform::gui::sdl {
-SdlOpenGLRenderer::SdlOpenGLRenderer(SdlWindow* window) {
+namespace {
+constexpr float kVertices[] = {-1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, -1.0f,
+ 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
+ 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f};
+constexpr unsigned int kIndices[] = {0, 1, 2, 0, 2, 3};
+
+constexpr auto kVertexShader = R"(#version 330 core
+layout (location = 0) in vec3 aPos;
+layout (location = 1) in vec2 aTexCoord;
+
+out vec2 TexCoord;
+
+void main()
+{
+ gl_Position = vec4(aPos, 1.0);
+ TexCoord = aTexCoord;
+})";
+
+constexpr auto kFragmentShader = R"(#version 330 core
+out vec4 FragColor;
+
+in vec2 TexCoord;
+
+uniform sampler2D ourTexture;
+
+void main()
+{
+ FragColor = texture(ourTexture, TexCoord);
+})";
+} // namespace
+
+SdlOpenGLRenderer::SdlOpenGLRenderer(SdlWindow* window, int width, int height) {
assert(window);
assert(window->GetSdlWindow());
@@ -12,20 +49,201 @@ SdlOpenGLRenderer::SdlOpenGLRenderer(SdlWindow* window) {
sdl_window_id_ = window->GetSdlWindowId();
sdl_gl_context_ = SDL_GL_CreateContext(sdl_window_);
+ gl_texture_ = 0;
+ cairo_surface_ = nullptr;
+ cairo_ = nullptr;
if (!sdl_gl_context_) {
throw SdlException("Failed to create sdl gl context.");
}
- CheckSdlReturn(SDL_GL_MakeCurrent(sdl_window_, sdl_gl_context_));
+ auto _ = MakeContextCurrent();
auto version = gladLoadGLContext(&glad_gl_context_, SDL_GL_GetProcAddress);
- CRU_LOG_TAG_DEBUG("SDL window id {}, openGL version: {}.", sdl_window_id_,
- version);
+ CRU_LOG_TAG_DEBUG("SDL window id {}, openGL version: {}.{}.", sdl_window_id_,
+ GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));
+
+ glad_gl_context_.Enable(GL_BLEND);
+ glad_gl_context_.BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ gl_shader_program_ = CreateGLProgram();
+
+ glad_gl_context_.GenVertexArrays(1, &gl_vertex_array_);
+ glad_gl_context_.GenBuffers(1, &gl_vertex_buffer_);
+ glad_gl_context_.GenBuffers(1, &gl_element_buffer_);
+
+ glad_gl_context_.BindVertexArray(gl_vertex_array_);
+
+ glad_gl_context_.BindBuffer(GL_ARRAY_BUFFER, gl_vertex_buffer_);
+ glad_gl_context_.BufferData(GL_ARRAY_BUFFER, sizeof(kVertices),
+ static_cast<const void*>(kVertices),
+ GL_STATIC_DRAW);
+
+ glad_gl_context_.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_element_buffer_);
+ glad_gl_context_.BufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kIndices),
+ static_cast<const void*>(kIndices),
+ GL_STATIC_DRAW);
+
+ glad_gl_context_.VertexAttribPointer(
+ 0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), static_cast<void*>(0));
+ glad_gl_context_.EnableVertexAttribArray(0);
+ glad_gl_context_.VertexAttribPointer(
+ 1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float),
+ reinterpret_cast<void*>(3 * sizeof(float)));
+ glad_gl_context_.EnableVertexAttribArray(1);
+
+ Resize(width, height);
}
SdlOpenGLRenderer::~SdlOpenGLRenderer() {
+ glad_gl_context_.DeleteBuffers(1, &gl_vertex_buffer_);
+ glad_gl_context_.DeleteBuffers(1, &gl_element_buffer_);
+ glad_gl_context_.DeleteTextures(1, &gl_texture_);
+
+ if (cairo_) {
+ cairo_destroy(cairo_);
+ cairo_ = nullptr;
+ }
+
+ if (cairo_surface_) {
+ cairo_surface_destroy(cairo_surface_);
+ cairo_surface_ = nullptr;
+ }
+
CheckSdlReturn(SDL_GL_DestroyContext(sdl_gl_context_));
}
+void SdlOpenGLRenderer::Resize(int width, int height) {
+ CRU_LOG_TAG_DEBUG("Resize to {} x {}", width, height);
+
+ width_ = width;
+ height_ = height;
+
+ auto _ = MakeContextCurrent();
+
+ if (cairo_) {
+ cairo_destroy(cairo_);
+ cairo_ = nullptr;
+ }
+
+ if (cairo_surface_) {
+ cairo_surface_destroy(cairo_surface_);
+ cairo_surface_ = nullptr;
+ }
+
+ if (gl_texture_) {
+ glad_gl_context_.DeleteTextures(1, &gl_texture_);
+ gl_texture_ = 0;
+ }
+
+ cairo_surface_ =
+ cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ auto status = cairo_surface_status(cairo_surface_);
+ if (status != CAIRO_STATUS_SUCCESS) {
+ throw Exception("Failed to create cairo surface.");
+ }
+
+ cairo_ = cairo_create(cairo_surface_);
+ status = cairo_status(cairo_);
+ if (status != CAIRO_STATUS_SUCCESS) {
+ throw Exception("Failed to create cairo context.");
+ }
+
+ glad_gl_context_.Viewport(0, 0, width, height);
+
+ glad_gl_context_.GenTextures(1, &gl_texture_);
+ glad_gl_context_.BindTexture(GL_TEXTURE_2D, gl_texture_);
+ glad_gl_context_.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ GL_NEAREST);
+ glad_gl_context_.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ GL_NEAREST);
+}
+
+std::unique_ptr<graphics::IPainter> SdlOpenGLRenderer::BeginPaint() {
+ assert(cairo_surface_);
+ assert(gl_texture_);
+
+ auto painter = std::make_unique<graphics::cairo::CairoPainter>(
+ nullptr, cairo_, false, cairo_surface_);
+ painter->SetEndDrawCallback([this] { Present(); });
+ return painter;
+}
+
+void SdlOpenGLRenderer::Present() {
+ assert(cairo_surface_);
+ assert(gl_texture_);
+
+ auto _ = MakeContextCurrent();
+
+ auto data = cairo_image_surface_get_data(cairo_surface_);
+ glad_gl_context_.TexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, width_, height_, 0,
+ GL_BGRA, GL_UNSIGNED_BYTE, data);
+
+ glad_gl_context_.UseProgram(gl_shader_program_);
+ glad_gl_context_.ActiveTexture(GL_TEXTURE0);
+ glad_gl_context_.BindTexture(GL_TEXTURE_2D, gl_texture_);
+ glad_gl_context_.BindVertexArray(gl_vertex_array_);
+ glad_gl_context_.BindBuffer(GL_ARRAY_BUFFER, gl_vertex_buffer_);
+ glad_gl_context_.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_element_buffer_);
+
+ glad_gl_context_.DrawElements(
+ GL_TRIANGLES, sizeof(kIndices) / sizeof(*kIndices), GL_UNSIGNED_INT, 0);
+ CheckSdlReturn(SDL_GL_SwapWindow(sdl_window_));
+}
+
+GLuint SdlOpenGLRenderer::CreateGLProgram() {
+ auto check_shader = [this](std::string_view name, GLuint shader) {
+ int success;
+ char infoLog[512];
+ glad_gl_context_.GetShaderiv(shader, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glad_gl_context_.GetShaderInfoLog(shader, 512, nullptr, infoLog);
+ CRU_LOG_TAG_ERROR("Failed to compile OpenGL {} shader: {}", name,
+ infoLog);
+ }
+ };
+
+ auto check_program = [this](std::string_view name, GLuint program) {
+ int success;
+ char infoLog[512];
+ glad_gl_context_.GetProgramiv(program, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glad_gl_context_.GetProgramInfoLog(program, 512, nullptr, infoLog);
+ CRU_LOG_TAG_ERROR("Failed to link OpenGL {} program: {}", name, infoLog);
+ }
+ };
+
+ auto vertex_shader = glad_gl_context_.CreateShader(GL_VERTEX_SHADER);
+ glad_gl_context_.ShaderSource(vertex_shader, 1, &kVertexShader, nullptr);
+ glad_gl_context_.CompileShader(vertex_shader);
+ check_shader("vertex", vertex_shader);
+
+ auto fragment_shader = glad_gl_context_.CreateShader(GL_FRAGMENT_SHADER);
+ glad_gl_context_.ShaderSource(fragment_shader, 1, &kFragmentShader, nullptr);
+ glad_gl_context_.CompileShader(fragment_shader);
+ check_shader("fragment", fragment_shader);
+
+ auto shader_program = glad_gl_context_.CreateProgram();
+ glad_gl_context_.AttachShader(shader_program, vertex_shader);
+ glad_gl_context_.AttachShader(shader_program, fragment_shader);
+ glad_gl_context_.LinkProgram(shader_program);
+ check_program("main", shader_program);
+
+ glad_gl_context_.DeleteShader(fragment_shader);
+ glad_gl_context_.DeleteShader(vertex_shader);
+
+ return shader_program;
+}
+
+Guard SdlOpenGLRenderer::MakeContextCurrent() {
+ auto old_context = SDL_GL_GetCurrentContext();
+ auto old_window = SDL_GL_GetCurrentWindow();
+
+ CheckSdlReturn(SDL_GL_MakeCurrent(sdl_window_, sdl_gl_context_));
+
+ return Guard([old_context, old_window] {
+ CheckSdlReturn(SDL_GL_MakeCurrent(old_window, old_context));
+ });
+}
+
} // namespace cru::platform::gui::sdl