diff --git a/Userland/Libraries/LibAccelGfx/CMakeLists.txt b/Userland/Libraries/LibAccelGfx/CMakeLists.txt index 74744c2ce9..94ec615d6b 100644 --- a/Userland/Libraries/LibAccelGfx/CMakeLists.txt +++ b/Userland/Libraries/LibAccelGfx/CMakeLists.txt @@ -2,6 +2,7 @@ include(accelerated_graphics) if (HAS_ACCELERATED_GRAPHICS) set(SOURCES + GL.cpp Canvas.cpp Context.cpp Painter.cpp diff --git a/Userland/Libraries/LibAccelGfx/Canvas.cpp b/Userland/Libraries/LibAccelGfx/Canvas.cpp index 9bfddc1b1e..622f88a06a 100644 --- a/Userland/Libraries/LibAccelGfx/Canvas.cpp +++ b/Userland/Libraries/LibAccelGfx/Canvas.cpp @@ -4,8 +4,8 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include "Canvas.h" -#include +#include +#include #include 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() diff --git a/Userland/Libraries/LibAccelGfx/GL.cpp b/Userland/Libraries/LibAccelGfx/GL.cpp new file mode 100644 index 0000000000..9dad447bd2 --- /dev/null +++ b/Userland/Libraries/LibAccelGfx/GL.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2023, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#define GL_GLEXT_PROTOTYPES + +#include +#include +#include + +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 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(); +} + +} diff --git a/Userland/Libraries/LibAccelGfx/GL.h b/Userland/Libraries/LibAccelGfx/GL.h new file mode 100644 index 0000000000..a69dba0c4f --- /dev/null +++ b/Userland/Libraries/LibAccelGfx/GL.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +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 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); + +} diff --git a/Userland/Libraries/LibAccelGfx/Painter.cpp b/Userland/Libraries/LibAccelGfx/Painter.cpp index 1522f04abb..82f310be65 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.cpp +++ b/Userland/Libraries/LibAccelGfx/Painter.cpp @@ -5,13 +5,10 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#define GL_GLEXT_PROTOTYPES - -#include "Painter.h" -#include "Canvas.h" #include -#include -#include +#include +#include +#include #include #include @@ -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> const& unique_glyphs) @@ -314,8 +289,7 @@ void Painter::prepare_glyph_texture(HashMap> 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 const& glyph_run, Color const& color) @@ -379,21 +353,15 @@ void Painter::draw_glyph_run(Vector 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() diff --git a/Userland/Libraries/LibAccelGfx/Painter.h b/Userland/Libraries/LibAccelGfx/Painter.h index 686e9c538d..e6b059936c 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.h +++ b/Userland/Libraries/LibAccelGfx/Painter.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -89,7 +90,7 @@ private: HashMap m_glyphs_texture_map; Gfx::IntSize m_glyphs_texture_size; - GLuint m_glyphs_texture; + GL::Texture m_glyphs_texture; }; } diff --git a/Userland/Libraries/LibAccelGfx/Program.cpp b/Userland/Libraries/LibAccelGfx/Program.cpp index dab717f157..111fcf6f17 100644 --- a/Userland/Libraries/LibAccelGfx/Program.cpp +++ b/Userland/Libraries/LibAccelGfx/Program.cpp @@ -4,78 +4,41 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#define GL_GLEXT_PROTOTYPES - #include #include -#include -#include +#include #include 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); } } diff --git a/Userland/Libraries/LibAccelGfx/Program.h b/Userland/Libraries/LibAccelGfx/Program.h index 2de71da82d..2a5a62dd84 100644 --- a/Userland/Libraries/LibAccelGfx/Program.h +++ b/Userland/Libraries/LibAccelGfx/Program.h @@ -7,7 +7,7 @@ #pragma once #include -#include +#include 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; }; }