diff --git a/Userland/Libraries/LibAccelGfx/GL.cpp b/Userland/Libraries/LibAccelGfx/GL.cpp index 7efd10ae14..787a6e8528 100644 --- a/Userland/Libraries/LibAccelGfx/GL.cpp +++ b/Userland/Libraries/LibAccelGfx/GL.cpp @@ -114,7 +114,7 @@ Texture create_texture() GLuint texture; glGenTextures(1, &texture); verify_no_error(); - return { texture }; + return { texture, {} }; } void bind_texture(Texture const& texture) @@ -123,11 +123,12 @@ void bind_texture(Texture const& texture) verify_no_error(); } -void upload_texture_data(Texture const& texture, Gfx::Bitmap const& bitmap) +void upload_texture_data(Texture& 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)); + texture.size = bitmap.size(); verify_no_error(); } @@ -231,7 +232,7 @@ Framebuffer create_framebuffer(Gfx::IntSize size) GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size.width(), size.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); GLuint fbo; glGenFramebuffers(1, &fbo); @@ -244,7 +245,7 @@ Framebuffer create_framebuffer(Gfx::IntSize size) verify_no_error(); - return { fbo, texture }; + return { fbo, Texture { texture, size } }; } void bind_framebuffer(Framebuffer const& framebuffer) @@ -257,7 +258,7 @@ void delete_framebuffer(Framebuffer const& framebuffer) { glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.fbo_id); glDeleteFramebuffers(1, &framebuffer.fbo_id); - glDeleteTextures(1, &framebuffer.texture_id); + delete_texture(framebuffer.texture); verify_no_error(); } diff --git a/Userland/Libraries/LibAccelGfx/GL.h b/Userland/Libraries/LibAccelGfx/GL.h index a3cb68f388..14d1d0cdb7 100644 --- a/Userland/Libraries/LibAccelGfx/GL.h +++ b/Userland/Libraries/LibAccelGfx/GL.h @@ -15,6 +15,7 @@ # include #endif #include +#include namespace AccelGfx::GL { @@ -41,6 +42,7 @@ struct Uniform { struct Texture { GLuint id; + Optional size; }; struct Buffer { @@ -53,7 +55,7 @@ struct VertexArray { struct Framebuffer { GLuint fbo_id; - GLuint texture_id; + GL::Texture texture; }; void set_viewport(Gfx::IntRect); @@ -70,7 +72,7 @@ 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 upload_texture_data(Texture& texture, Gfx::Bitmap const& bitmap); void delete_texture(Texture const&); void set_uniform(Uniform const& uniform, float, float); diff --git a/Userland/Libraries/LibAccelGfx/Painter.cpp b/Userland/Libraries/LibAccelGfx/Painter.cpp index 54b76b29ba..4e220f2ca2 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.cpp +++ b/Userland/Libraries/LibAccelGfx/Painter.cpp @@ -142,6 +142,16 @@ OwnPtr Painter::create() return make(context); } +OwnPtr Painter::create_with_glyphs_texture_from_painter(Painter const& painter) +{ + auto& context = Context::the(); + auto glyphs_texture_painter = make(context); + glyphs_texture_painter->m_glyphs_texture = painter.m_glyphs_texture; + glyphs_texture_painter->m_glyphs_texture_size = painter.m_glyphs_texture_size; + glyphs_texture_painter->m_glyphs_texture_map = painter.m_glyphs_texture_map; + return glyphs_texture_painter; +} + Painter::Painter(Context& context) : m_context(context) , m_rectangle_program(Program::create(Program::Name::RectangleProgram, vertex_shader_source, solid_color_fragment_shader_source)) @@ -333,54 +343,11 @@ static GL::ScalingMode to_gl_scaling_mode(Painter::ScalingMode scaling_mode) void Painter::draw_scaled_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::FloatRect const& src_rect, ScalingMode scaling_mode) { - bind_target_canvas(); - - m_blit_program.use(); - // FIXME: We should reuse textures across repaints if possible. auto texture = GL::create_texture(); GL::upload_texture_data(texture, bitmap); - - 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()); - - Vector vertices; - vertices.ensure_capacity(16); - - auto add_vertex = [&](auto const& p, auto const& s) { - vertices.append(p.x()); - vertices.append(p.y()); - vertices.append(s.x()); - vertices.append(s.y()); - }; - - add_vertex(dst_rect_in_clip_space.top_left(), src_rect_in_texture_space.top_left()); - add_vertex(dst_rect_in_clip_space.bottom_left(), src_rect_in_texture_space.bottom_left()); - 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()); - - auto vbo = GL::create_buffer(); - GL::upload_to_buffer(vbo, vertices); - - auto vao = GL::create_vertex_array(); - GL::bind_vertex_array(vao); - GL::bind_buffer(vbo); - - auto vertex_position_attribute = m_blit_program.get_attribute_location("aVertexPosition"); - GL::set_vertex_attribute(vertex_position_attribute, 0, 4); - - auto color_uniform = m_blit_program.get_uniform_location("uColor"); - GL::set_uniform(color_uniform, 1, 1, 1, 1); - - GL::enable_blending(); - GL::draw_arrays(GL::DrawPrimitive::TriangleFan, 4); - + blit_scaled_texture(dst_rect, texture, src_rect, scaling_mode); GL::delete_texture(texture); - GL::delete_buffer(vbo); - GL::delete_vertex_array(vao); } void Painter::prepare_glyph_texture(HashMap> const& unique_glyphs) @@ -662,4 +629,65 @@ void Painter::flush(Gfx::Bitmap& bitmap) GL::read_pixels({ 0, 0, bitmap.width(), bitmap.height() }, bitmap); } +void Painter::blit_canvas(Gfx::IntRect const& dst_rect, Canvas const& canvas, float opacity) +{ + blit_canvas(dst_rect.to_type(), canvas, opacity); +} + +void Painter::blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const& canvas, float opacity) +{ + auto texture = GL::Texture(canvas.framebuffer().texture); + blit_scaled_texture(dst_rect, texture, { { 0, 0 }, canvas.size() }, Painter::ScalingMode::NearestNeighbor, opacity); +} + +void Painter::blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture const& texture, Gfx::FloatRect const& src_rect, ScalingMode scaling_mode, float opacity) +{ + bind_target_canvas(); + + m_blit_program.use(); + + auto dst_rect_in_clip_space = to_clip_space(transform().map(dst_rect)); + auto src_rect_in_texture_space = to_texture_space(src_rect, *texture.size); + + Vector vertices; + vertices.ensure_capacity(16); + + auto add_vertex = [&](auto const& p, auto const& s) { + vertices.append(p.x()); + vertices.append(p.y()); + vertices.append(s.x()); + vertices.append(s.y()); + }; + + add_vertex(dst_rect_in_clip_space.top_left(), src_rect_in_texture_space.top_left()); + add_vertex(dst_rect_in_clip_space.bottom_left(), src_rect_in_texture_space.bottom_left()); + 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()); + + auto vbo = GL::create_buffer(); + GL::upload_to_buffer(vbo, vertices); + + auto vao = GL::create_vertex_array(); + GL::bind_vertex_array(vao); + GL::bind_buffer(vbo); + + auto vertex_position_attribute = m_blit_program.get_attribute_location("aVertexPosition"); + GL::set_vertex_attribute(vertex_position_attribute, 0, 4); + + auto color_uniform = m_blit_program.get_uniform_location("uColor"); + GL::set_uniform(color_uniform, 1, 1, 1, opacity); + + GL::bind_texture(texture); + + auto scaling_mode_gl = to_gl_scaling_mode(scaling_mode); + GL::set_texture_scale_mode(scaling_mode_gl); + + GL::enable_blending(); + + GL::draw_arrays(GL::DrawPrimitive::TriangleFan, 4); + + GL::delete_buffer(vbo); + GL::delete_vertex_array(vao); +} + } diff --git a/Userland/Libraries/LibAccelGfx/Painter.h b/Userland/Libraries/LibAccelGfx/Painter.h index ed65255f14..e2d71195b4 100644 --- a/Userland/Libraries/LibAccelGfx/Painter.h +++ b/Userland/Libraries/LibAccelGfx/Painter.h @@ -29,6 +29,7 @@ class Painter { public: static OwnPtr create(); + static OwnPtr create_with_glyphs_texture_from_painter(Painter const& painter); Painter(Context&); ~Painter(); @@ -86,6 +87,9 @@ public: void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, CornerRadius const& top_left_radius, CornerRadius const& top_right_radius, CornerRadius const& bottom_left_radius, CornerRadius const& bottom_right_radius); void fill_rect_with_rounded_corners(Gfx::FloatRect const& rect, Color const& color, CornerRadius const& top_left_radius, CornerRadius const& top_right_radius, CornerRadius const& bottom_left_radius, CornerRadius const& bottom_right_radius); + void blit_canvas(Gfx::IntRect const& dst_rect, Canvas const&, float opacity = 1.0f); + void blit_canvas(Gfx::FloatRect const& dst_rect, Canvas const&, float opacity = 1.0f); + private: Context& m_context; @@ -96,6 +100,7 @@ private: [[nodiscard]] State& state() { return m_state_stack.last(); } [[nodiscard]] State const& state() const { return m_state_stack.last(); } + void blit_scaled_texture(Gfx::FloatRect const& dst_rect, GL::Texture const&, Gfx::FloatRect const& src_rect, ScalingMode, float opacity = 1.0f); void bind_target_canvas(); [[nodiscard]] Gfx::FloatRect to_clip_space(Gfx::FloatRect const& screen_rect) const; diff --git a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp index 663e44f2f3..9cbfb51ec1 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.cpp @@ -8,13 +8,22 @@ namespace Web::Painting { -PaintingCommandExecutorGPU::PaintingCommandExecutorGPU(AccelGfx::Painter& painter) - : m_painter(painter) +PaintingCommandExecutorGPU::PaintingCommandExecutorGPU(Gfx::Bitmap& bitmap) + : m_target_bitmap(bitmap) { + auto painter = AccelGfx::Painter::create(); + auto canvas = AccelGfx::Canvas::create(bitmap.size()); + painter->set_target_canvas(canvas); + stacking_contexts.append({ .canvas = canvas, + .painter = move(painter), + .opacity = 1.0f, + .destination = {} }); } PaintingCommandExecutorGPU::~PaintingCommandExecutorGPU() { + VERIFY(stacking_contexts.size() == 1); + painter().flush(m_target_bitmap); } CommandResult PaintingCommandExecutorGPU::draw_glyph_run(Vector const& glyph_run, Color const& color) @@ -75,16 +84,32 @@ CommandResult PaintingCommandExecutorGPU::set_font(Gfx::Font const&) return CommandResult::Continue; } -CommandResult PaintingCommandExecutorGPU::push_stacking_context(float, bool, Gfx::IntRect const&, Gfx::IntPoint post_transform_translation, CSS::ImageRendering, StackingContextTransform, Optional) +CommandResult PaintingCommandExecutorGPU::push_stacking_context(float opacity, bool, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering, StackingContextTransform, Optional) { - painter().save(); - painter().translate(post_transform_translation.to_type()); + if (opacity < 1) { + auto painter = AccelGfx::Painter::create_with_glyphs_texture_from_painter(this->painter()); + auto canvas = AccelGfx::Canvas::create(source_paintable_rect.size()); + painter->set_target_canvas(canvas); + painter->translate(-source_paintable_rect.location().to_type()); + stacking_contexts.append({ .canvas = canvas, + .painter = move(painter), + .opacity = opacity, + .destination = source_paintable_rect }); + } else { + painter().save(); + painter().translate(post_transform_translation.to_type()); + } return CommandResult::Continue; } CommandResult PaintingCommandExecutorGPU::pop_stacking_context() { - painter().restore(); + if (stacking_contexts.last().opacity < 1) { + auto stacking_context = stacking_contexts.take_last(); + painter().blit_canvas(stacking_context.destination, *stacking_context.canvas, stacking_context.opacity); + } else { + painter().restore(); + } return CommandResult::Continue; } @@ -283,7 +308,7 @@ bool PaintingCommandExecutorGPU::would_be_fully_clipped_by_painter(Gfx::IntRect) void PaintingCommandExecutorGPU::prepare_glyph_texture(HashMap> const& unique_glyphs) { - m_painter.prepare_glyph_texture(unique_glyphs); + painter().prepare_glyph_texture(unique_glyphs); } } diff --git a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h index 54b490d065..66ed48f664 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h +++ b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorGPU.h @@ -51,13 +51,23 @@ public: virtual bool needs_prepare_glyphs_texture() const override { return true; } void prepare_glyph_texture(HashMap> const&) override; - PaintingCommandExecutorGPU(AccelGfx::Painter& painter); + PaintingCommandExecutorGPU(Gfx::Bitmap& bitmap); ~PaintingCommandExecutorGPU() override; private: - AccelGfx::Painter& painter() { return m_painter; } + Gfx::Bitmap& m_target_bitmap; - AccelGfx::Painter& m_painter; + struct StackingContext { + RefPtr canvas; + OwnPtr painter; + float opacity; + Gfx::IntRect destination; + }; + + [[nodiscard]] AccelGfx::Painter const& painter() const { return *stacking_contexts.last().painter; } + [[nodiscard]] AccelGfx::Painter& painter() { return *stacking_contexts.last().painter; } + + Vector stacking_contexts; }; } diff --git a/Userland/Services/WebContent/PageHost.cpp b/Userland/Services/WebContent/PageHost.cpp index 3ac822ee03..7e0b478578 100644 --- a/Userland/Services/WebContent/PageHost.cpp +++ b/Userland/Services/WebContent/PageHost.cpp @@ -43,12 +43,6 @@ PageHost::PageHost(ConnectionFromClient& client) m_client.async_did_invalidate_content_rect({ m_invalidation_rect.x().value(), m_invalidation_rect.y().value(), m_invalidation_rect.width().value(), m_invalidation_rect.height().value() }); m_invalidation_rect = {}; }); - - if (s_use_gpu_painter) { -#ifdef HAS_ACCELERATED_GRAPHICS - m_accelerated_painter = AccelGfx::Painter::create(); -#endif - } } PageHost::~PageHost() = default; @@ -164,10 +158,8 @@ void PageHost::paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap& targ if (s_use_gpu_painter) { #ifdef HAS_ACCELERATED_GRAPHICS - m_accelerated_painter->set_target_canvas(AccelGfx::Canvas::create(target.size())); - Web::Painting::PaintingCommandExecutorGPU painting_command_executor(*m_accelerated_painter); + Web::Painting::PaintingCommandExecutorGPU painting_command_executor(target); recording_painter.execute(painting_command_executor); - m_accelerated_painter->flush(target); #endif } else { Web::Painting::PaintingCommandExecutorCPU painting_command_executor(target); diff --git a/Userland/Services/WebContent/PageHost.h b/Userland/Services/WebContent/PageHost.h index 9425f473e6..ddbd6c5f26 100644 --- a/Userland/Services/WebContent/PageHost.h +++ b/Userland/Services/WebContent/PageHost.h @@ -143,10 +143,6 @@ private: Web::CSS::PreferredColorScheme m_preferred_color_scheme { Web::CSS::PreferredColorScheme::Auto }; RefPtr m_webdriver; - -#ifdef HAS_ACCELERATED_GRAPHICS - OwnPtr m_accelerated_painter; -#endif }; }