mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibGfx+LibWeb: Create PaintingSurface snapshot without GPU->RAM readback
...by constructing ImmutableBitmap directly from SkImage. This is a huge optimization for the case when content of canvas is painted onto another canvas, as it allows pixels to remain in GPU memory throughout the process. Fixes performance regression on https://playbiolab.com/ introduced by switching to GPU-backend for canvas.
This commit is contained in:
parent
1a01a71568
commit
4b93e27698
Notes:
github-actions[bot]
2024-11-09 20:20:52 +00:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/LadybirdBrowser/ladybird/commit/4b93e276985 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2254
7 changed files with 29 additions and 21 deletions
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <LibGfx/ImmutableBitmap.h>
|
#include <LibGfx/ImmutableBitmap.h>
|
||||||
|
#include <LibGfx/PaintingSurface.h>
|
||||||
|
|
||||||
#include <core/SkBitmap.h>
|
#include <core/SkBitmap.h>
|
||||||
#include <core/SkImage.h>
|
#include <core/SkImage.h>
|
||||||
|
@ -14,7 +15,7 @@ namespace Gfx {
|
||||||
struct ImmutableBitmapImpl {
|
struct ImmutableBitmapImpl {
|
||||||
sk_sp<SkImage> sk_image;
|
sk_sp<SkImage> sk_image;
|
||||||
SkBitmap sk_bitmap;
|
SkBitmap sk_bitmap;
|
||||||
RefPtr<Gfx::Bitmap> gfx_bitmap;
|
Variant<NonnullRefPtr<Gfx::Bitmap>, NonnullRefPtr<Gfx::PaintingSurface>, Empty> source;
|
||||||
};
|
};
|
||||||
|
|
||||||
int ImmutableBitmap::width() const
|
int ImmutableBitmap::width() const
|
||||||
|
@ -49,15 +50,14 @@ SkImage const* ImmutableBitmap::sk_image() const
|
||||||
|
|
||||||
RefPtr<Gfx::Bitmap const> ImmutableBitmap::bitmap() const
|
RefPtr<Gfx::Bitmap const> ImmutableBitmap::bitmap() const
|
||||||
{
|
{
|
||||||
return m_impl->gfx_bitmap;
|
// FIXME: Implement for PaintingSurface
|
||||||
|
return m_impl->source.get<NonnullRefPtr<Gfx::Bitmap>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Color ImmutableBitmap::get_pixel(int x, int y) const
|
Color ImmutableBitmap::get_pixel(int x, int y) const
|
||||||
{
|
{
|
||||||
if (m_impl->gfx_bitmap) {
|
// FIXME: Implement for PaintingSurface
|
||||||
return m_impl->gfx_bitmap->get_pixel(x, y);
|
return m_impl->source.get<NonnullRefPtr<Gfx::Bitmap>>()->get_pixel(x, y);
|
||||||
}
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static SkColorType to_skia_color_type(Gfx::BitmapFormat format)
|
static SkColorType to_skia_color_type(Gfx::BitmapFormat format)
|
||||||
|
@ -96,7 +96,15 @@ NonnullRefPtr<ImmutableBitmap> ImmutableBitmap::create(NonnullRefPtr<Bitmap> bit
|
||||||
impl.sk_bitmap.installPixels(info, const_cast<void*>(static_cast<void const*>(bitmap->scanline(0))), bitmap->pitch());
|
impl.sk_bitmap.installPixels(info, const_cast<void*>(static_cast<void const*>(bitmap->scanline(0))), bitmap->pitch());
|
||||||
impl.sk_bitmap.setImmutable();
|
impl.sk_bitmap.setImmutable();
|
||||||
impl.sk_image = impl.sk_bitmap.asImage();
|
impl.sk_image = impl.sk_bitmap.asImage();
|
||||||
impl.gfx_bitmap = bitmap;
|
impl.source = bitmap;
|
||||||
|
return adopt_ref(*new ImmutableBitmap(make<ImmutableBitmapImpl>(impl)));
|
||||||
|
}
|
||||||
|
|
||||||
|
NonnullRefPtr<ImmutableBitmap> ImmutableBitmap::create_snapshot_from_painting_surface(NonnullRefPtr<PaintingSurface> painting_surface)
|
||||||
|
{
|
||||||
|
ImmutableBitmapImpl impl;
|
||||||
|
impl.sk_image = painting_surface->sk_image_snapshot<sk_sp<SkImage>>();
|
||||||
|
impl.source = painting_surface;
|
||||||
return adopt_ref(*new ImmutableBitmap(make<ImmutableBitmapImpl>(impl)));
|
return adopt_ref(*new ImmutableBitmap(make<ImmutableBitmapImpl>(impl)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ struct ImmutableBitmapImpl;
|
||||||
class ImmutableBitmap final : public RefCounted<ImmutableBitmap> {
|
class ImmutableBitmap final : public RefCounted<ImmutableBitmap> {
|
||||||
public:
|
public:
|
||||||
static NonnullRefPtr<ImmutableBitmap> create(NonnullRefPtr<Bitmap> bitmap);
|
static NonnullRefPtr<ImmutableBitmap> create(NonnullRefPtr<Bitmap> bitmap);
|
||||||
|
static NonnullRefPtr<ImmutableBitmap> create_snapshot_from_painting_surface(NonnullRefPtr<PaintingSurface>);
|
||||||
|
|
||||||
~ImmutableBitmap();
|
~ImmutableBitmap();
|
||||||
|
|
||||||
|
|
|
@ -92,15 +92,6 @@ PaintingSurface::PaintingSurface(NonnullOwnPtr<Impl>&& impl)
|
||||||
|
|
||||||
PaintingSurface::~PaintingSurface() = default;
|
PaintingSurface::~PaintingSurface() = default;
|
||||||
|
|
||||||
NonnullRefPtr<ImmutableBitmap> PaintingSurface::create_snapshot() const
|
|
||||||
{
|
|
||||||
auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size()).value();
|
|
||||||
auto image_info = SkImageInfo::Make(bitmap->width(), bitmap->height(), kBGRA_8888_SkColorType, kPremul_SkAlphaType);
|
|
||||||
SkPixmap const pixmap(image_info, bitmap->begin(), bitmap->pitch());
|
|
||||||
sk_surface().readPixels(pixmap, 0, 0);
|
|
||||||
return ImmutableBitmap::create(bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PaintingSurface::read_into_bitmap(Gfx::Bitmap& bitmap)
|
void PaintingSurface::read_into_bitmap(Gfx::Bitmap& bitmap)
|
||||||
{
|
{
|
||||||
auto color_type = to_skia_color_type(bitmap.format());
|
auto color_type = to_skia_color_type(bitmap.format());
|
||||||
|
@ -130,6 +121,12 @@ SkSurface& PaintingSurface::sk_surface() const
|
||||||
return *m_impl->surface;
|
return *m_impl->surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
sk_sp<SkImage> PaintingSurface::sk_image_snapshot() const
|
||||||
|
{
|
||||||
|
return m_impl->surface->makeImageSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
void PaintingSurface::flush() const
|
void PaintingSurface::flush() const
|
||||||
{
|
{
|
||||||
if (auto context = m_impl->context) {
|
if (auto context = m_impl->context) {
|
||||||
|
|
|
@ -31,7 +31,6 @@ public:
|
||||||
static NonnullRefPtr<PaintingSurface> wrap_metal_surface(Gfx::MetalTexture&, RefPtr<SkiaBackendContext>);
|
static NonnullRefPtr<PaintingSurface> wrap_metal_surface(Gfx::MetalTexture&, RefPtr<SkiaBackendContext>);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NonnullRefPtr<ImmutableBitmap> create_snapshot() const;
|
|
||||||
void read_into_bitmap(Bitmap&);
|
void read_into_bitmap(Bitmap&);
|
||||||
|
|
||||||
IntSize size() const;
|
IntSize size() const;
|
||||||
|
@ -40,6 +39,9 @@ public:
|
||||||
SkCanvas& canvas() const;
|
SkCanvas& canvas() const;
|
||||||
SkSurface& sk_surface() const;
|
SkSurface& sk_surface() const;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T sk_image_snapshot() const;
|
||||||
|
|
||||||
void flush() const;
|
void flush() const;
|
||||||
|
|
||||||
~PaintingSurface();
|
~PaintingSurface();
|
||||||
|
|
|
@ -131,7 +131,7 @@ WebIDL::ExceptionOr<JS::GCPtr<CanvasPattern>> CanvasPattern::create(JS::Realm& r
|
||||||
auto bitmap = image.visit(
|
auto bitmap = image.visit(
|
||||||
[](JS::Handle<HTMLImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->immutable_bitmap(); },
|
[](JS::Handle<HTMLImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->immutable_bitmap(); },
|
||||||
[](JS::Handle<SVG::SVGImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->current_image_bitmap(); },
|
[](JS::Handle<SVG::SVGImageElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->current_image_bitmap(); },
|
||||||
[](JS::Handle<HTMLCanvasElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return source->surface()->create_snapshot(); },
|
[](JS::Handle<HTMLCanvasElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*source->surface()); },
|
||||||
[](JS::Handle<HTMLVideoElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); },
|
[](JS::Handle<HTMLVideoElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); },
|
||||||
[](JS::Handle<ImageBitmap> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); });
|
[](JS::Handle<ImageBitmap> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); });
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,7 @@ WebIDL::ExceptionOr<void> CanvasRenderingContext2D::draw_image_internal(CanvasIm
|
||||||
auto surface = source->surface();
|
auto surface = source->surface();
|
||||||
if (!surface)
|
if (!surface)
|
||||||
return {};
|
return {};
|
||||||
return source->surface()->create_snapshot();
|
return Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*surface);
|
||||||
},
|
},
|
||||||
[](JS::Handle<HTMLVideoElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); },
|
[](JS::Handle<HTMLVideoElement> const& source) -> RefPtr<Gfx::ImmutableBitmap> { return Gfx::ImmutableBitmap::create(*source->bitmap()); },
|
||||||
[](JS::Handle<ImageBitmap> const& source) -> RefPtr<Gfx::ImmutableBitmap> {
|
[](JS::Handle<ImageBitmap> const& source) -> RefPtr<Gfx::ImmutableBitmap> {
|
||||||
|
@ -401,7 +401,7 @@ WebIDL::ExceptionOr<JS::GCPtr<ImageData>> CanvasRenderingContext2D::get_image_da
|
||||||
// NOTE: We don't attempt to create the underlying bitmap here; if it doesn't exist, it's like copying only transparent black pixels (which is a no-op).
|
// NOTE: We don't attempt to create the underlying bitmap here; if it doesn't exist, it's like copying only transparent black pixels (which is a no-op).
|
||||||
if (!canvas_element().surface())
|
if (!canvas_element().surface())
|
||||||
return image_data;
|
return image_data;
|
||||||
auto const snapshot = canvas_element().surface()->create_snapshot();
|
auto const snapshot = Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*canvas_element().surface());
|
||||||
|
|
||||||
// 5. Let the source rectangle be the rectangle whose corners are the four points (sx, sy), (sx+sw, sy), (sx+sw, sy+sh), (sx, sy+sh).
|
// 5. Let the source rectangle be the rectangle whose corners are the four points (sx, sy), (sx+sw, sy), (sx+sw, sy+sh), (sx, sy+sh).
|
||||||
auto source_rect = Gfx::Rect { x, y, abs_width, abs_height };
|
auto source_rect = Gfx::Rect { x, y, abs_width, abs_height };
|
||||||
|
|
|
@ -268,7 +268,7 @@ String HTMLCanvasElement::to_data_url(StringView type, Optional<double> quality)
|
||||||
return "data:,"_string;
|
return "data:,"_string;
|
||||||
|
|
||||||
// 3. Let file be a serialization of this canvas element's bitmap as a file, passing type and quality if given.
|
// 3. Let file be a serialization of this canvas element's bitmap as a file, passing type and quality if given.
|
||||||
auto snapshot = m_surface->create_snapshot();
|
auto snapshot = Gfx::ImmutableBitmap::create_snapshot_from_painting_surface(*m_surface);
|
||||||
auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, m_surface->size()));
|
auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, m_surface->size()));
|
||||||
m_surface->read_into_bitmap(*bitmap);
|
m_surface->read_into_bitmap(*bitmap);
|
||||||
auto file = serialize_bitmap(bitmap, type, move(quality));
|
auto file = serialize_bitmap(bitmap, type, move(quality));
|
||||||
|
|
Loading…
Reference in a new issue