mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
LibAccelGfx+LibWeb: Add support for stacking context opacity
For each stacking context with an opacity less than 1, we create a separate framebuffer. We then blit the texture attached to this framebuffer with the specified opacity. To avoid the performance overhead of reading pixels from the texture into Gfx::Bitmap, a new method that allows for direct blitting from the texture is introduced, named blit_scaled_texture().
This commit is contained in:
parent
cb90daadc7
commit
5f7ac559a7
Notes:
sideshowbarker
2024-07-17 04:49:48 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/5f7ac559a7 Pull-request: https://github.com/SerenityOS/serenity/pull/22035
8 changed files with 133 additions and 74 deletions
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# include <GL/gl.h>
|
||||
#endif
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
|
||||
namespace AccelGfx::GL {
|
||||
|
||||
|
@ -41,6 +42,7 @@ struct Uniform {
|
|||
|
||||
struct Texture {
|
||||
GLuint id;
|
||||
Optional<Gfx::IntSize> 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);
|
||||
|
|
|
@ -142,6 +142,16 @@ OwnPtr<Painter> Painter::create()
|
|||
return make<Painter>(context);
|
||||
}
|
||||
|
||||
OwnPtr<Painter> Painter::create_with_glyphs_texture_from_painter(Painter const& painter)
|
||||
{
|
||||
auto& context = Context::the();
|
||||
auto glyphs_texture_painter = make<Painter>(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<GLfloat> 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<Gfx::Font const*, HashTable<u32>> 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<float>(), 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<GLfloat> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ class Painter {
|
|||
|
||||
public:
|
||||
static OwnPtr<Painter> create();
|
||||
static OwnPtr<Painter> 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;
|
||||
|
|
|
@ -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<Gfx::DrawGlyphOrEmoji> 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<StackingContextMask>)
|
||||
CommandResult PaintingCommandExecutorGPU::push_stacking_context(float opacity, bool, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering, StackingContextTransform, Optional<StackingContextMask>)
|
||||
{
|
||||
painter().save();
|
||||
painter().translate(post_transform_translation.to_type<float>());
|
||||
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<float>());
|
||||
stacking_contexts.append({ .canvas = canvas,
|
||||
.painter = move(painter),
|
||||
.opacity = opacity,
|
||||
.destination = source_paintable_rect });
|
||||
} else {
|
||||
painter().save();
|
||||
painter().translate(post_transform_translation.to_type<float>());
|
||||
}
|
||||
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<Gfx::Font const*, HashTable<u32>> const& unique_glyphs)
|
||||
{
|
||||
m_painter.prepare_glyph_texture(unique_glyphs);
|
||||
painter().prepare_glyph_texture(unique_glyphs);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,13 +51,23 @@ public:
|
|||
virtual bool needs_prepare_glyphs_texture() const override { return true; }
|
||||
void prepare_glyph_texture(HashMap<Gfx::Font const*, HashTable<u32>> 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<AccelGfx::Canvas> canvas;
|
||||
OwnPtr<AccelGfx::Painter> 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<StackingContext> stacking_contexts;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -143,10 +143,6 @@ private:
|
|||
Web::CSS::PreferredColorScheme m_preferred_color_scheme { Web::CSS::PreferredColorScheme::Auto };
|
||||
|
||||
RefPtr<WebDriverConnection> m_webdriver;
|
||||
|
||||
#ifdef HAS_ACCELERATED_GRAPHICS
|
||||
OwnPtr<AccelGfx::Painter> m_accelerated_painter;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue