LibAccelGfx: Use wrapping functions with error check for OpenGL calls

This change introduces GL.h with error check wrappers for all the
OpenGL functions we used so far.

For now, the error check is simply:
`VERIFY(glGetError() == GL_NO_ERROR);`
but that is better than continuing execution after encounting an error.
This commit is contained in:
Aliaksandr Kalenik 2023-11-10 18:00:18 +01:00 committed by Andreas Kling
parent da88d766b2
commit 048e179572
Notes: sideshowbarker 2024-07-17 04:34:25 +09:00
8 changed files with 313 additions and 130 deletions

View file

@ -2,6 +2,7 @@ include(accelerated_graphics)
if (HAS_ACCELERATED_GRAPHICS)
set(SOURCES
GL.cpp
Canvas.cpp
Context.cpp
Painter.cpp

View file

@ -4,8 +4,8 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "Canvas.h"
#include <GL/gl.h>
#include <LibAccelGfx/Canvas.h>
#include <LibAccelGfx/GL.h>
#include <LibGfx/Bitmap.h>
namespace AccelGfx {
@ -28,13 +28,12 @@ void Canvas::initialize()
{
m_surface = m_context.create_surface(width(), height());
m_context.set_active_surface(m_surface);
glViewport(0, 0, width(), height());
GL::set_viewport({ 0, 0, width(), height() });
}
void Canvas::flush()
{
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, width(), height(), GL_BGRA, GL_UNSIGNED_BYTE, m_bitmap->scanline(0));
GL::read_pixels({ 0, 0, width(), height() }, *m_bitmap);
}
Canvas::~Canvas()

View file

@ -0,0 +1,176 @@
/*
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#define GL_GLEXT_PROTOTYPES
#include <LibAccelGfx/GL.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Rect.h>
namespace AccelGfx::GL {
static void verify_no_error()
{
VERIFY(glGetError() == GL_NO_ERROR);
}
void set_viewport(Gfx::IntRect rect)
{
glViewport(rect.left(), rect.top(), rect.width(), rect.height());
verify_no_error();
}
void enable_blending()
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
verify_no_error();
}
void read_pixels(Gfx::IntRect rect, Gfx::Bitmap& bitmap)
{
VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRA8888);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(rect.left(), rect.top(), rect.width(), rect.height(), GL_BGRA, GL_UNSIGNED_BYTE, bitmap.scanline(0));
verify_no_error();
}
Shader create_shader(ShaderType type, char const* source)
{
GLuint shader = glCreateShader(type == ShaderType::Vertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);
int success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char buffer[512];
glGetShaderInfoLog(shader, sizeof(buffer), nullptr, buffer);
dbgln("GLSL shader compilation failed: {}", buffer);
VERIFY_NOT_REACHED();
}
verify_no_error();
return { shader };
}
Program create_program(Shader const& vertex_shader, Shader const& fragment_shader)
{
GLuint program = glCreateProgram();
glAttachShader(program, vertex_shader.id);
glAttachShader(program, fragment_shader.id);
glLinkProgram(program);
int linked;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if (!linked) {
char buffer[512];
glGetProgramInfoLog(program, sizeof(buffer), nullptr, buffer);
dbgln("GLSL program linking failed: {}", buffer);
VERIFY_NOT_REACHED();
}
glDeleteShader(vertex_shader.id);
glDeleteShader(fragment_shader.id);
verify_no_error();
return { program };
}
void use_program(Program const& program)
{
glUseProgram(program.id);
verify_no_error();
}
VertexAttribute get_attribute_location(Program const& program, char const* name)
{
auto id = glGetAttribLocation(program.id, name);
verify_no_error();
return { id };
}
Uniform get_uniform_location(Program const& program, char const* name)
{
auto id = glGetUniformLocation(program.id, name);
verify_no_error();
return { id };
}
void delete_program(Program const& program)
{
glDeleteProgram(program.id);
verify_no_error();
}
Texture create_texture()
{
GLuint texture;
glGenTextures(1, &texture);
verify_no_error();
return { texture };
}
void bind_texture(Texture const& texture)
{
glBindTexture(GL_TEXTURE_2D, texture.id);
verify_no_error();
}
void upload_texture_data(Texture const& texture, Gfx::Bitmap const& bitmap)
{
VERIFY(bitmap.format() == Gfx::BitmapFormat::BGRx8888 || bitmap.format() == Gfx::BitmapFormat::BGRA8888);
bind_texture(texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap.width(), bitmap.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, bitmap.scanline(0));
verify_no_error();
}
void delete_texture(Texture const& texture)
{
glDeleteTextures(1, &texture.id);
verify_no_error();
}
void set_uniform(Uniform const& uniform, float value1, float value2, float value3, float value4)
{
glUniform4f(uniform.id, value1, value2, value3, value4);
verify_no_error();
}
void set_vertex_attribute(VertexAttribute const& attribute, Span<float> values, int number_of_components)
{
glVertexAttribPointer(attribute.id, number_of_components, GL_FLOAT, GL_FALSE, number_of_components * sizeof(float), values.data());
glEnableVertexAttribArray(attribute.id);
verify_no_error();
}
void set_texture_scale_mode(ScalingMode scaling_mode)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, scaling_mode == ScalingMode::Nearest ? GL_NEAREST : GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, scaling_mode == ScalingMode::Nearest ? GL_NEAREST : GL_LINEAR);
verify_no_error();
}
void clear_color(Gfx::Color const& color)
{
glClearColor(color.red() / 255.0f, color.green() / 255.0f, color.blue() / 255.0f, color.alpha() / 255.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
verify_no_error();
}
void draw_arrays(DrawPrimitive draw_primitive, size_t count)
{
GLenum mode = GL_TRIANGLES;
if (draw_primitive == DrawPrimitive::TriangleFan)
mode = GL_TRIANGLE_FAN;
glDrawArrays(mode, 0, count);
verify_no_error();
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Vector.h>
#include <GL/gl.h>
#include <LibGfx/Forward.h>
namespace AccelGfx::GL {
enum class ShaderType {
Vertex,
Fragment,
};
struct Shader {
GLuint id;
};
struct Program {
GLuint id;
};
struct VertexAttribute {
GLint id;
};
struct Uniform {
GLint id;
};
struct Texture {
GLuint id;
};
void set_viewport(Gfx::IntRect);
void enable_blending();
void read_pixels(Gfx::IntRect, Gfx::Bitmap&);
Shader create_shader(ShaderType type, char const* source);
Program create_program(Shader const& vertex_shader, Shader const& fragment_shader);
void use_program(Program const&);
VertexAttribute get_attribute_location(Program const&, char const* name);
Uniform get_uniform_location(Program const&, char const* name);
void delete_program(Program const&);
Texture create_texture();
void bind_texture(Texture const&);
void upload_texture_data(Texture const& texture, Gfx::Bitmap const& bitmap);
void delete_texture(Texture const&);
void set_uniform(Uniform const& uniform, float, float, float, float);
void set_vertex_attribute(VertexAttribute const& attribute, Span<float> values, int number_of_components);
enum class ScalingMode {
Nearest,
Linear,
};
void set_texture_scale_mode(ScalingMode);
void clear_color(Gfx::Color const&);
enum class DrawPrimitive {
Triangles,
TriangleFan,
};
void draw_arrays(DrawPrimitive, size_t count);
}

View file

@ -5,13 +5,10 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#define GL_GLEXT_PROTOTYPES
#include "Painter.h"
#include "Canvas.h"
#include <AK/QuickSort.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <LibAccelGfx/Canvas.h>
#include <LibAccelGfx/GL.h>
#include <LibAccelGfx/Painter.h>
#include <LibGfx/Color.h>
#include <LibGfx/Painter.h>
@ -89,10 +86,9 @@ Painter::Painter(Context& context)
: m_context(context)
, m_rectangle_program(Program::create(vertex_shader_source, solid_color_fragment_shader_source))
, m_blit_program(Program::create(blit_vertex_shader_source, blit_fragment_shader_source))
, m_glyphs_texture(GL::create_texture())
{
m_state_stack.empend(State());
glGenTextures(1, &m_glyphs_texture);
}
Painter::~Painter()
@ -102,9 +98,7 @@ Painter::~Painter()
void Painter::clear(Gfx::Color color)
{
auto [red, green, blue, alpha] = gfx_color_to_opengl_color(color);
glClearColor(red, green, blue, alpha);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GL::clear_color(color);
}
void Painter::fill_rect(Gfx::IntRect rect, Gfx::Color color)
@ -136,18 +130,13 @@ void Painter::fill_rect(Gfx::FloatRect rect, Gfx::Color color)
m_rectangle_program.use();
GLuint position_attribute = m_rectangle_program.get_attribute_location("aVertexPosition");
GLuint color_uniform = m_rectangle_program.get_uniform_location("uColor");
auto position_attribute = m_rectangle_program.get_attribute_location("aVertexPosition");
auto color_uniform = m_rectangle_program.get_uniform_location("uColor");
glUniform4f(color_uniform, red, green, blue, alpha);
glVertexAttribPointer(position_attribute, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), vertices.data());
glEnableVertexAttribArray(position_attribute);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
GL::set_uniform(color_uniform, red, green, blue, alpha);
GL::set_vertex_attribute(position_attribute, vertices, 2);
GL::enable_blending();
GL::draw_arrays(GL::DrawPrimitive::TriangleFan, 4);
}
void Painter::draw_line(Gfx::IntPoint a, Gfx::IntPoint b, float thickness, Gfx::Color color)
@ -172,18 +161,13 @@ void Painter::draw_line(Gfx::FloatPoint a, Gfx::FloatPoint b, float thickness, C
m_rectangle_program.use();
GLuint position_attribute = m_rectangle_program.get_attribute_location("aVertexPosition");
GLuint color_uniform = m_rectangle_program.get_uniform_location("uColor");
auto position_attribute = m_rectangle_program.get_attribute_location("aVertexPosition");
auto color_uniform = m_rectangle_program.get_uniform_location("uColor");
glUniform4f(color_uniform, red, green, blue, alpha);
glVertexAttribPointer(position_attribute, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), vertices.data());
glEnableVertexAttribArray(position_attribute);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
GL::set_uniform(color_uniform, red, green, blue, alpha);
GL::set_vertex_attribute(position_attribute, vertices, 2);
GL::enable_blending();
GL::draw_arrays(GL::DrawPrimitive::TriangleFan, 4);
}
void Painter::draw_scaled_bitmap(Gfx::IntRect const& dest_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, ScalingMode scaling_mode)
@ -201,13 +185,13 @@ static Gfx::FloatRect to_texture_space(Gfx::FloatRect rect, Gfx::IntSize image_s
return { x, y, width, height };
}
static GLenum to_gl_scaling_mode(Painter::ScalingMode scaling_mode)
static GL::ScalingMode to_gl_scaling_mode(Painter::ScalingMode scaling_mode)
{
switch (scaling_mode) {
case Painter::ScalingMode::NearestNeighbor:
return GL_NEAREST;
return GL::ScalingMode::Nearest;
case Painter::ScalingMode::Bilinear:
return GL_LINEAR;
return GL::ScalingMode::Linear;
default:
VERIFY_NOT_REACHED();
}
@ -217,19 +201,12 @@ void Painter::draw_scaled_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap con
{
m_blit_program.use();
GLuint texture;
// FIXME: We should reuse textures across repaints if possible.
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, bitmap.width(), bitmap.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, bitmap.scanline(0));
auto texture = GL::create_texture();
GL::upload_texture_data(texture, bitmap);
GLenum scaling_mode_gl = to_gl_scaling_mode(scaling_mode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, scaling_mode_gl);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, scaling_mode_gl);
auto scaling_mode_gl = to_gl_scaling_mode(scaling_mode);
GL::set_texture_scale_mode(scaling_mode_gl);
auto dst_rect_in_clip_space = to_clip_space(transform().map(dst_rect));
auto src_rect_in_texture_space = to_texture_space(src_rect, bitmap.size());
@ -249,18 +226,16 @@ void Painter::draw_scaled_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap con
add_vertex(dst_rect_in_clip_space.bottom_right(), src_rect_in_texture_space.bottom_right());
add_vertex(dst_rect_in_clip_space.top_right(), src_rect_in_texture_space.top_right());
GLuint vertex_position_attribute = m_blit_program.get_attribute_location("aVertexPosition");
glVertexAttribPointer(vertex_position_attribute, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), vertices.data());
glEnableVertexAttribArray(vertex_position_attribute);
auto vertex_position_attribute = m_blit_program.get_attribute_location("aVertexPosition");
GL::set_vertex_attribute(vertex_position_attribute, vertices, 4);
GLuint color_uniform = m_blit_program.get_uniform_location("uColor");
glUniform4f(color_uniform, 1, 1, 1, 1);
auto color_uniform = m_blit_program.get_uniform_location("uColor");
GL::set_uniform(color_uniform, 1, 1, 1, 1);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
GL::enable_blending();
GL::draw_arrays(GL::DrawPrimitive::TriangleFan, 4);
glDeleteTextures(1, &texture);
GL::delete_texture(texture);
}
void Painter::prepare_glyph_texture(HashMap<Gfx::Font const*, HashTable<u32>> const& unique_glyphs)
@ -314,8 +289,7 @@ void Painter::prepare_glyph_texture(HashMap<Gfx::Font const*, HashTable<u32>> co
m_glyphs_texture_size = glyphs_texture_bitmap->size();
glBindTexture(GL_TEXTURE_2D, m_glyphs_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, glyphs_texture_bitmap->width(), glyphs_texture_bitmap->height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, glyphs_texture_bitmap->scanline(0));
GL::upload_texture_data(m_glyphs_texture, *glyphs_texture_bitmap);
}
void Painter::draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const& color)
@ -379,21 +353,15 @@ void Painter::draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Col
m_blit_program.use();
glBindTexture(GL_TEXTURE_2D, m_glyphs_texture);
GL::bind_texture(m_glyphs_texture);
GL::set_texture_scale_mode(GL::ScalingMode::Nearest);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
auto position_attribute = m_blit_program.get_attribute_location("aVertexPosition");
auto color_uniform = m_blit_program.get_uniform_location("uColor");
GLuint position_attribute = m_blit_program.get_attribute_location("aVertexPosition");
GLuint color_uniform = m_blit_program.get_uniform_location("uColor");
glUniform4f(color_uniform, red, green, blue, alpha);
glVertexAttribPointer(position_attribute, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), vertices.data());
glEnableVertexAttribArray(position_attribute);
glDrawArrays(GL_TRIANGLES, 0, vertices.size() / 4);
GL::set_uniform(color_uniform, red, green, blue, alpha);
GL::set_vertex_attribute(position_attribute, vertices, 4);
GL::draw_arrays(GL::DrawPrimitive::Triangles, vertices.size() / 4);
}
void Painter::save()

View file

@ -12,6 +12,7 @@
#include <AK/Vector.h>
#include <LibAccelGfx/Canvas.h>
#include <LibAccelGfx/Forward.h>
#include <LibAccelGfx/GL.h>
#include <LibAccelGfx/Program.h>
#include <LibGfx/AffineTransform.h>
#include <LibGfx/Font/Font.h>
@ -89,7 +90,7 @@ private:
HashMap<GlyphsTextureKey, Gfx::IntRect> m_glyphs_texture_map;
Gfx::IntSize m_glyphs_texture_size;
GLuint m_glyphs_texture;
GL::Texture m_glyphs_texture;
};
}

View file

@ -4,78 +4,41 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#define GL_GLEXT_PROTOTYPES
#include <AK/Assertions.h>
#include <AK/Format.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <LibAccelGfx/GL.h>
#include <LibAccelGfx/Program.h>
namespace AccelGfx {
static GLuint create_shader(GLenum type, char const* source)
{
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);
int success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char buffer[512];
glGetShaderInfoLog(shader, sizeof(buffer), nullptr, buffer);
dbgln("GLSL shader compilation failed: {}", buffer);
VERIFY_NOT_REACHED();
}
return shader;
}
Program Program::create(char const* vertex_shader_source, char const* fragment_shader_source)
{
GLuint program = glCreateProgram();
auto vertex_shader = GL::create_shader(GL::ShaderType::Vertex, vertex_shader_source);
auto fragment_shader = GL::create_shader(GL::ShaderType::Fragment, fragment_shader_source);
auto vertex_shader = create_shader(GL_VERTEX_SHADER, vertex_shader_source);
auto fragment_shader = create_shader(GL_FRAGMENT_SHADER, fragment_shader_source);
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glLinkProgram(program);
int linked;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if (!linked) {
char buffer[512];
glGetProgramInfoLog(program, sizeof(buffer), nullptr, buffer);
dbgln("GLSL program linking failed: {}", buffer);
VERIFY_NOT_REACHED();
}
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
auto program = GL::create_program(vertex_shader, fragment_shader);
return Program { program };
}
void Program::use()
{
glUseProgram(m_id);
GL::use_program(m_program);
}
GLuint Program::get_attribute_location(char const* name)
GL::VertexAttribute Program::get_attribute_location(char const* name)
{
return glGetAttribLocation(m_id, name);
return GL::get_attribute_location(m_program, name);
}
GLuint Program::get_uniform_location(char const* name)
GL::Uniform Program::get_uniform_location(char const* name)
{
return glGetUniformLocation(m_id, name);
return GL::get_uniform_location(m_program, name);
}
Program::~Program()
{
glDeleteProgram(m_id);
GL::delete_program(m_program);
}
}

View file

@ -7,7 +7,7 @@
#pragma once
#include <AK/Noncopyable.h>
#include <GL/gl.h>
#include <LibAccelGfx/GL.h>
namespace AccelGfx {
@ -18,18 +18,18 @@ public:
static Program create(char const* vertex_shader_source, char const* fragment_shader_source);
void use();
GLuint get_attribute_location(char const* name);
GLuint get_uniform_location(char const* name);
GL::VertexAttribute get_attribute_location(char const* name);
GL::Uniform get_uniform_location(char const* name);
~Program();
private:
Program(GLuint id)
: m_id(id)
Program(GL::Program program)
: m_program(program)
{
}
GLuint m_id { 0 };
GL::Program m_program;
};
}