LibWeb+Compositor: Run canvas contexts in the Compositor

Canvas rendering is a major remaining path where WebContent directly
owns GPU-facing drawing state. Back 2D and WebGL canvas contexts with
remote Compositor transports, so WebContent talks to canvas surfaces
through IPC while the Compositor owns the rasterization resources.

This is a large step toward GPU sandboxing because canvas GPU work now
lives behind the Compositor boundary. It also gives OffscreenCanvas the
process-independent canvas plumbing that HTMLCanvasElement now uses,
making worker-owned canvases possible without another WebContent-local
rendering path.
This commit is contained in:
Aliaksandr Kalenik 2026-06-15 19:37:51 +02:00 committed by Alexander Kalenik
parent 5f0e95de13
commit a80babffb6
57 changed files with 888 additions and 832 deletions

View file

@ -5,6 +5,7 @@
*/
#include <LibGfx/CanvasCommandList.h>
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/Canvas/DrawingState.h>
#include <LibWeb/HTML/HTMLCanvasElement.h>
#include <LibWeb/HTML/OffscreenCanvas.h>

View file

@ -31,8 +31,6 @@ Gfx::IntSize canvas_image_source_dimensions(CanvasImageSource const& image)
return { source->width()->anim_val()->value(), source->height()->anim_val()->value() };
},
[](GC::Ref<HTMLCanvasElement> source) -> Gfx::IntSize {
if (auto painting_surface = source->surface())
return painting_surface->size();
return { source->width(), source->height() };
},
[](GC::Ref<ImageBitmap> source) -> Gfx::IntSize {
@ -67,11 +65,11 @@ Optional<Gfx::DecodedImageFrame> canvas_image_source_frame(CanvasImageSource con
return image_data->frame(0, size);
},
[](GC::Ref<HTMLCanvasElement> const& canvas) -> Optional<Gfx::DecodedImageFrame> {
canvas->present();
auto surface = canvas->surface();
if (!surface)
return Gfx::DecodedImageFrame { *canvas->get_bitmap_from_surface() };
return Gfx::DecodedImageFrame { *surface->snapshot_bitmap() };
canvas->prepare_for_compositing();
auto bitmap = canvas->get_bitmap_from_surface();
if (!bitmap)
return {};
return Gfx::DecodedImageFrame { *bitmap };
},
[](OneOf<GC::Ref<ImageBitmap>, GC::Ref<OffscreenCanvas>> auto const& source) -> Optional<Gfx::DecodedImageFrame> {
auto bitmap = source->bitmap();

View file

@ -17,7 +17,7 @@ public:
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(int width, int height, Optional<Bindings::ImageDataSettings> const& settings = {}) const = 0;
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(ImageData const&) const = 0;
virtual WebIDL::ExceptionOr<GC::Ptr<ImageData>> get_image_data(int x, int y, int width, int height, Optional<Bindings::ImageDataSettings> const& settings = {}) const = 0;
virtual WebIDL::ExceptionOr<GC::Ptr<ImageData>> get_image_data(int x, int y, int width, int height, Optional<Bindings::ImageDataSettings> const& settings = {}) = 0;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y) = 0;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) = 0;

View file

@ -20,11 +20,12 @@ class WEB_API RemoteCanvas2DTransport : public RefCounted<RemoteCanvas2DTranspor
public:
virtual ~RemoteCanvas2DTransport() = default;
virtual Optional<Painting::CanvasId> create_context(Gfx::IntSize, bool alpha) = 0;
virtual void destroy_context(Painting::CanvasId) = 0;
virtual void update_commands(Painting::CanvasId, Gfx::CanvasCommandList const&) = 0;
virtual bool create_context(Gfx::IntSize, bool alpha) = 0;
virtual Optional<Painting::CanvasId> canvas_id() const = 0;
virtual void destroy_context() = 0;
virtual void update_commands(Gfx::CanvasCommandList const&) = 0;
virtual RefPtr<Gfx::Bitmap> read_back_pixels(Painting::CanvasId, Gfx::IntRect const&) = 0;
virtual RefPtr<Gfx::Bitmap> read_back_pixels(Gfx::IntRect const&) = 0;
};
}

View file

@ -14,7 +14,6 @@
#include <AK/OwnPtr.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/CanvasCommandList.h>
#include <LibGfx/CanvasCommandPlayer.h>
#include <LibGfx/CompositingAndBlendingOperator.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/Painter.h>
@ -30,7 +29,9 @@
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/PropertyID.h>
#include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h>
#include <LibWeb/Compositor/CompositorHost.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/Canvas/RemoteCanvas2DTransport.h>
#include <LibWeb/HTML/CanvasRenderingContext2D.h>
#include <LibWeb/HTML/DecodedImageData.h>
#include <LibWeb/HTML/HTMLCanvasElement.h>
@ -40,16 +41,21 @@
#include <LibWeb/HTML/ImageBitmap.h>
#include <LibWeb/HTML/ImageData.h>
#include <LibWeb/HTML/ImageRequest.h>
#include <LibWeb/HTML/Navigable.h>
#include <LibWeb/HTML/Path2D.h>
#include <LibWeb/HTML/TextMetrics.h>
#include <LibWeb/Infra/CharacterTypes.h>
#include <LibWeb/Layout/TextNode.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/Painting/Paintable.h>
#include <LibWeb/SVG/SVGImageElement.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::HTML {
// Keep bitmap-heavy recorded command lists small enough to send to the Compositor without exceeding IPC attachment limits.
static constexpr size_t max_pending_canvas_commands = 64;
GC_DEFINE_ALLOCATOR(CanvasRenderingContext2D);
JS::ThrowCompletionOr<GC::Ref<CanvasRenderingContext2D>> CanvasRenderingContext2D::create(JS::Realm& realm, HTMLCanvasElement& element, JS::Value options)
@ -75,6 +81,12 @@ void CanvasRenderingContext2D::initialize(JS::Realm& realm)
set_prototype(&Bindings::ensure_web_prototype<Bindings::CanvasRenderingContext2DPrototype>(realm, "CanvasRenderingContext2D"_string));
}
void CanvasRenderingContext2D::finalize()
{
discard_backing_storage();
Base::finalize();
}
void CanvasRenderingContext2D::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
@ -85,10 +97,10 @@ void CanvasRenderingContext2D::visit_edges(Cell::Visitor& visitor)
size_t CanvasRenderingContext2D::external_memory_size() const
{
auto size = Base::external_memory_size();
if (!m_player)
if (!has_backing_storage())
return size;
auto surface_size = m_player->surface()->size();
auto surface_size = m_size;
if (surface_size.is_empty())
return size;
@ -162,7 +174,7 @@ WebIDL::ExceptionOr<void> CanvasRenderingContext2D::draw_image_internal(CanvasIm
auto frame = canvas_image_source_frame(image);
if (!frame.has_value())
return {};
auto const& bitmap = frame->bitmap();
auto source_bitmap_rect = frame->rect();
// 4. Establish the source and destination rectangles as follows:
// If not specified, the dw and dh arguments must default to the values of sw and sh, interpreted such that one CSS pixel in the image is treated as one unit in the output bitmap's coordinate space.
@ -195,7 +207,7 @@ WebIDL::ExceptionOr<void> CanvasRenderingContext2D::draw_image_internal(CanvasIm
auto destination_rect = Gfx::FloatRect { destination_x, destination_y, destination_width, destination_height };
// When the source rectangle is outside the source image, the source rectangle must be clipped
// to the source image and the destination rectangle must be clipped in the same proportion.
auto clipped_source = source_rect.intersected(bitmap.rect().to_type<float>());
auto clipped_source = source_rect.intersected(source_bitmap_rect.to_type<float>());
auto clipped_destination = destination_rect;
if (clipped_source != source_rect) {
clipped_destination.set_width(clipped_destination.width() * (clipped_source.width() / source_rect.width()));
@ -242,26 +254,53 @@ void CanvasRenderingContext2D::did_draw(Gfx::FloatRect const&)
Gfx::CanvasCommandList* CanvasRenderingContext2D::canvas_command_list()
{
allocate_painting_surface_if_needed();
if (!m_player)
if (is_context_lost())
return nullptr;
ensure_backing_storage();
if (!has_backing_storage())
return nullptr;
if (m_commands.size() >= max_pending_canvas_commands)
flush_recorded_commands();
return &m_commands;
}
RefPtr<Gfx::PaintingSurface> CanvasRenderingContext2D::surface()
bool CanvasRenderingContext2D::ensure_remote_canvas_context()
{
if (!m_player)
return nullptr;
flush_recorded_commands();
return m_player->surface();
if (m_transport)
return true;
auto& page = m_element->document().page();
if (!page.has_compositor_host())
return false;
auto transport = page.compositor_host().create_canvas_2d_transport();
if (!transport)
return false;
// FIXME: implement context attribute .color_space
// FIXME: implement context attribute .color_type
// FIXME: implement context attribute .desynchronized
// FIXME: implement context attribute .will_read_frequently
if (!transport->create_context(m_element->bitmap_size_for_canvas(), m_context_attributes.alpha))
return false;
m_transport = move(transport);
return true;
}
void CanvasRenderingContext2D::flush_recorded_commands()
{
if (m_commands.is_empty())
if (m_commands.is_empty() || !m_transport)
return;
auto commands = move(m_commands);
m_player->play(commands);
m_transport->update_commands(commands);
}
RefPtr<Gfx::Bitmap> CanvasRenderingContext2D::read_pixels(Gfx::IntRect const& rect)
{
if (!has_backing_storage())
return nullptr;
flush_recorded_commands();
return m_transport->read_back_pixels(rect);
}
void CanvasRenderingContext2D::set_size(Gfx::IntSize const& size)
@ -269,38 +308,38 @@ void CanvasRenderingContext2D::set_size(Gfx::IntSize const& size)
if (m_size == size)
return;
m_size = size;
m_commands = {};
m_player = nullptr;
discard_backing_storage();
}
void CanvasRenderingContext2D::present()
void CanvasRenderingContext2D::prepare_for_compositing()
{
if (!m_player)
return;
flush_recorded_commands();
}
void CanvasRenderingContext2D::allocate_painting_surface_if_needed()
Optional<Painting::CanvasId> CanvasRenderingContext2D::canvas_id() const
{
if (m_player || m_size.is_empty())
if (!m_transport)
return {};
return m_transport->canvas_id();
}
void CanvasRenderingContext2D::ensure_backing_storage()
{
if (has_backing_storage() || m_size.is_empty())
return;
if (!ensure_remote_canvas_context())
return;
// FIXME: implement context attribute .color_space
// FIXME: implement context attribute .color_type
// FIXME: implement context attribute .desynchronized
// FIXME: implement context attribute .will_read_frequently
auto color_type = m_context_attributes.alpha ? Gfx::BitmapFormat::BGRA8888 : Gfx::BitmapFormat::BGRx8888;
auto surface_size = m_element->bitmap_size_for_canvas();
m_player = make<Gfx::CanvasCommandPlayer>(nullptr, surface_size, color_type, Gfx::AlphaType::Premultiplied);
m_element->set_needs_repaint();
}
// https://html.spec.whatwg.org/multipage/canvas.html#the-canvas-settings:concept-canvas-alpha
// Thus, the bitmap of such a context starts off as opaque black instead of transparent black;
// AD-HOC: Skia provides us with a full transparent surface by default; only clear the surface if alpha is disabled.
if (!m_context_attributes.alpha)
m_player->clear(clear_color());
void CanvasRenderingContext2D::discard_backing_storage()
{
m_commands = {};
if (m_transport) {
m_transport->destroy_context();
m_transport = nullptr;
}
}
Gfx::Path CanvasRenderingContext2D::text_path(Utf16String const& text, float x, float y, Optional<double> max_width)
@ -585,7 +624,7 @@ WebIDL::ExceptionOr<GC::Ref<ImageData>> CanvasRenderingContext2D::create_image_d
}
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-getimagedata
WebIDL::ExceptionOr<GC::Ptr<ImageData>> CanvasRenderingContext2D::get_image_data(int x, int y, int width, int height, Optional<Bindings::ImageDataSettings> const& settings) const
WebIDL::ExceptionOr<GC::Ptr<ImageData>> CanvasRenderingContext2D::get_image_data(int x, int y, int width, int height, Optional<Bindings::ImageDataSettings> const& settings)
{
// 1. If either the sw or sh arguments are zero, then throw an "IndexSizeError" DOMException.
if (width == 0 || height == 0)
@ -604,12 +643,6 @@ WebIDL::ExceptionOr<GC::Ptr<ImageData>> CanvasRenderingContext2D::get_image_data
// 4. Initialize imageData given sw, sh, settings set to settings, and defaultColorSpace set to this's color space.
auto image_data = TRY(ImageData::create(realm(), abs_width, abs_height, settings));
// 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).
auto surface = m_element->surface();
if (!surface)
return image_data;
auto const snapshot = Gfx::DecodedImageFrame { *surface->snapshot_bitmap() };
// 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 };
@ -619,7 +652,16 @@ WebIDL::ExceptionOr<GC::Ptr<ImageData>> CanvasRenderingContext2D::get_image_data
if (width < 0 || height < 0) {
source_rect = source_rect.translated(min(width, 0), min(height, 0));
}
auto source_rect_intersected = source_rect.intersected(snapshot.rect());
auto source_rect_intersected = source_rect.intersected(Gfx::IntRect { {}, m_size });
if (source_rect_intersected.is_empty())
return image_data;
// NOTE: If reading back from the Compositor fails (no backing storage or no connection),
// it's like copying only transparent black pixels (which is a no-op).
auto pixels = read_pixels(source_rect_intersected);
if (!pixels)
return image_data;
auto const snapshot = Gfx::DecodedImageFrame { *pixels };
// 6. Set the pixel values of imageData to be the pixels of this's output bitmap in the area specified by the source rectangle in the bitmap's coordinate space units, converted from this's color space to imageData's colorSpace using 'relative-colorimetric' rendering intent.
// NOTE: Internally we must use premultiplied alpha, but ImageData should hold unpremultiplied alpha. This conversion
@ -629,7 +671,7 @@ WebIDL::ExceptionOr<GC::Ptr<ImageData>> CanvasRenderingContext2D::get_image_data
VERIFY(image_data->bitmap().alpha_type() == Gfx::AlphaType::Unpremultiplied);
auto painter = Gfx::Painter::create(image_data->bitmap());
painter->draw_bitmap(image_data->bitmap().rect().to_type<float>(), snapshot, source_rect_intersected, Gfx::ScalingMode::NearestNeighbor, {}, 1, Gfx::CompositingAndBlendingOperator::SourceOver);
painter->draw_bitmap(image_data->bitmap().rect().to_type<float>(), snapshot, snapshot.rect(), Gfx::ScalingMode::NearestNeighbor, {}, 1, Gfx::CompositingAndBlendingOperator::SourceOver);
// 7. Set the pixels values of imageData for areas of the source rectangle that are outside of the output bitmap to transparent black.
// NOTE: No-op, already done during creation.
@ -744,12 +786,11 @@ WebIDL::ExceptionOr<void> CanvasRenderingContext2D::put_pixels_from_an_image_dat
// https://html.spec.whatwg.org/multipage/canvas.html#reset-the-rendering-context-to-its-default-state
void CanvasRenderingContext2D::reset_to_default_state()
{
auto surface = m_element->surface();
auto* canvas_command_list = has_backing_storage() ? this->canvas_command_list() : nullptr;
// 1. Clear canvas's bitmap to transparent black.
if (surface) {
canvas_command_list()->append(Gfx::CanvasCommands::ClearRect { .rect = surface->rect().to_type<float>(), .color = clear_color() });
}
if (canvas_command_list)
canvas_command_list->append(Gfx::CanvasCommands::ClearRect { .rect = Gfx::FloatRect { {}, m_size.to_type<float>() }, .color = clear_color() });
// 2. Empty the list of subpaths in context's current default path.
path().clear();
@ -760,9 +801,9 @@ void CanvasRenderingContext2D::reset_to_default_state()
// 4. Reset everything that drawing state consists of to their initial values.
reset_drawing_state();
if (surface) {
canvas_command_list()->append(Gfx::CanvasCommands::Reset {});
did_draw(surface->rect().to_type<float>());
if (canvas_command_list) {
canvas_command_list->append(Gfx::CanvasCommands::Reset {});
did_draw(Gfx::FloatRect { {}, m_size.to_type<float>() });
}
}

View file

@ -8,12 +8,13 @@
#pragma once
#include <AK/Optional.h>
#include <AK/String.h>
#include <LibGfx/CanvasCommandList.h>
#include <LibGfx/Forward.h>
#include <LibGfx/Path.h>
#include <LibGfx/TextLayout.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/Compositor/Types.h>
#include <LibWeb/HTML/Canvas/CanvasCompositing.h>
#include <LibWeb/HTML/Canvas/CanvasDrawImage.h>
#include <LibWeb/HTML/Canvas/CanvasDrawPath.h>
@ -57,6 +58,8 @@ class CanvasRenderingContext2D
GC_DECLARE_ALLOCATOR(CanvasRenderingContext2D);
public:
static constexpr bool OVERRIDES_FINALIZE = true;
static JS::ThrowCompletionOr<GC::Ref<CanvasRenderingContext2D>> create(JS::Realm&, HTMLCanvasElement&, JS::Value options);
virtual ~CanvasRenderingContext2D() override;
@ -78,7 +81,7 @@ public:
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(int width, int height, Optional<Bindings::ImageDataSettings> const& settings = {}) const override;
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(ImageData const& image_data) const override;
virtual WebIDL::ExceptionOr<GC::Ptr<ImageData>> get_image_data(int x, int y, int width, int height, Optional<Bindings::ImageDataSettings> const& settings = {}) const override;
virtual WebIDL::ExceptionOr<GC::Ptr<ImageData>> get_image_data(int x, int y, int width, int height, Optional<Bindings::ImageDataSettings> const& settings = {}) override;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y) override;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) override;
WebIDL::ExceptionOr<void> put_pixels_from_an_image_data_onto_a_bitmap(ImageData&, Gfx::CanvasCommandList&, float dx, float dy, float dirty_x, float dirty_y, float dirty_width, float dirty_height);
@ -121,10 +124,15 @@ public:
virtual void set_shadow_color(String) override;
void set_size(Gfx::IntSize const&);
void present();
void prepare_for_compositing();
RefPtr<Gfx::PaintingSurface> surface();
void allocate_painting_surface_if_needed();
void ensure_backing_storage();
void discard_backing_storage();
Optional<Painting::CanvasId> canvas_id() const;
RefPtr<Gfx::Bitmap> read_pixels(Gfx::IntRect const&);
protected:
[[nodiscard]] Gfx::CanvasCommandList* canvas_command_list() override;
@ -139,6 +147,7 @@ private:
virtual bool is_canvas_rendering_context_2d() const final { return true; }
virtual void initialize(JS::Realm&) override;
virtual void finalize() override;
virtual void visit_edges(Cell::Visitor&) override;
virtual size_t external_memory_size() const override;
@ -167,10 +176,14 @@ private:
void flush_recorded_commands();
bool ensure_remote_canvas_context();
bool has_backing_storage() const { return m_transport != nullptr; }
GC::Ref<HTMLCanvasElement> m_element;
Gfx::CanvasCommandList m_commands;
OwnPtr<Gfx::CanvasCommandPlayer> m_player;
RefPtr<RemoteCanvas2DTransport> m_transport;
// https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-origin-clean
bool m_origin_clean { true };

View file

@ -524,10 +524,10 @@ void EventLoop::update_the_rendering()
// FIXME: 21. For each doc of docs, mark paint timing for doc.
// AD-HOC: Present all canvas element surfaces in documents' pages after callbacks
// AD-HOC: Flush dirty canvas contexts in documents' pages after callbacks
// have had a chance to update them, and before painting snapshots the frame.
for (auto& document : docs)
document->page().present_all_canvas_element_surfaces();
document->page().prepare_canvas_contexts_for_compositing();
// 22. For each doc of docs, update the rendering or user interface of doc and its node navigable to reflect the current state.
for (auto& doc : docs.in_reverse()) {

View file

@ -7,6 +7,7 @@
#include <AK/Base64.h>
#include <AK/Checked.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/CanvasCommandList.h>
#include <LibGfx/SharedImage.h>
#include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/HTMLCanvasElement.h>
@ -29,6 +30,7 @@
#include <LibWeb/Platform/EventLoopPlugin.h>
#include <LibWeb/Platform/FontPlugin.h>
#include <LibWeb/WebGL/WebGL2RenderingContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContext.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
@ -36,7 +38,13 @@ namespace Web::HTML {
GC_DEFINE_ALLOCATOR(HTMLCanvasElement);
static constexpr auto max_canvas_area = 16384 * 16384;
static RefPtr<Gfx::Bitmap> create_transparent_canvas_bitmap(Gfx::IntSize const& size)
{
auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size);
if (bitmap_or_error.is_error())
return nullptr;
return bitmap_or_error.release_value();
}
HTMLCanvasElement::HTMLCanvasElement(DOM::Document& document, DOM::QualifiedName qualified_name)
: HTMLElement(document, move(qualified_name))
@ -54,7 +62,10 @@ void HTMLCanvasElement::initialize(JS::Realm& realm)
void HTMLCanvasElement::finalize()
{
clear_compositor_surface();
// The remote canvas context belongs to the 2D context; tear it down with the
// element, since nothing will reach the context afterwards.
if (auto context = canvas_rendering_context_2d())
context->discard_backing_storage();
Base::finalize();
document().page().unregister_canvas_element({}, unique_id());
}
@ -132,16 +143,8 @@ WebIDL::UnsignedLong HTMLCanvasElement::height() const
return 150;
}
Painting::CompositorSurfaceId HTMLCanvasElement::ensure_compositor_surface_id()
{
if (!m_compositor_surface_id.has_value())
m_compositor_surface_id = Painting::allocate_compositor_surface_id();
return *m_compositor_surface_id;
}
void HTMLCanvasElement::reset_context_to_default_state()
{
clear_compositor_surface();
m_context.visit(
[](GC::Ref<CanvasRenderingContext2D>& context) {
context->reset_to_default_state();
@ -323,7 +326,7 @@ Gfx::IntSize HTMLCanvasElement::bitmap_size_for_canvas(size_t minimum_width, siz
dbgln("Refusing to create {}x{} canvas (overflow)", width, height);
return {};
}
if (area.value() > max_canvas_area) {
if (area.value() > Gfx::max_canvas_area) {
dbgln("Refusing to create {}x{} canvas (exceeds maximum size)", width, height);
return {};
}
@ -333,26 +336,17 @@ Gfx::IntSize HTMLCanvasElement::bitmap_size_for_canvas(size_t minimum_width, siz
// https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-todataurl
String HTMLCanvasElement::to_data_url(StringView type, Optional<JS::Value> js_quality)
{
// It is possible the canvas doesn't have an associated bitmap so create one
allocate_painting_surface_if_needed();
auto surface = this->surface();
auto size = bitmap_size_for_canvas();
if (!surface && !size.is_empty()) {
// If the context is not initialized yet, we need to allocate transparent surface for serialization
surface = Gfx::PaintingSurface::create_with_size(size, Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
}
// FIXME: 1. If this canvas element's bitmap's origin-clean flag is set to false, then throw a "SecurityError" DOMException.
// 2. If this canvas element's bitmap has no pixels (i.e. either its horizontal dimension or its vertical dimension is zero),
// then return the string "data:,". (This is the shortest data: URL; it represents the empty string in a text/plain resource.)
if (!surface)
auto bitmap = get_bitmap_from_surface();
if (!bitmap)
return "data:,"_string;
// 3. Let file be a serialization of this canvas element's bitmap as a file, passing type and quality if given.
auto bitmap = surface->snapshot_bitmap();
Optional<double> quality = js_quality.has_value() && js_quality->is_number() ? js_quality->as_double() : Optional<double>();
auto file = serialize_bitmap(bitmap, type, quality);
auto file = serialize_bitmap(*bitmap, type, quality);
// 4. If file is null, then return "data:,".
if (file.is_error()) {
@ -408,19 +402,40 @@ WebIDL::ExceptionOr<void> HTMLCanvasElement::to_blob(GC::Ref<WebIDL::CallbackTyp
return {};
}
WebGL::WebGLRenderingContextBase* HTMLCanvasElement::webgl_context() const
{
return m_context.visit(
[](GC::Ref<WebGL::WebGLRenderingContext> const& context) -> WebGL::WebGLRenderingContextBase* { return context.ptr(); },
[](GC::Ref<WebGL::WebGL2RenderingContext> const& context) -> WebGL::WebGLRenderingContextBase* { return context.ptr(); },
[](auto const&) -> WebGL::WebGLRenderingContextBase* { return nullptr; });
}
Optional<Painting::CanvasId> HTMLCanvasElement::canvas_id() const
{
if (auto context = canvas_rendering_context_2d())
return context->canvas_id();
if (auto* webgl_context = this->webgl_context(); webgl_context && !webgl_context->is_context_lost())
return webgl_context->context().canvas_id();
return {};
}
RefPtr<Gfx::Bitmap> HTMLCanvasElement::get_bitmap_from_surface()
{
// It is possible the canvas doesn't have an associated bitmap so create one
allocate_painting_surface_if_needed();
auto surface = this->surface();
if (auto const size = bitmap_size_for_canvas(); !surface && !size.is_empty()) {
// If the context is not initialized yet, we need to allocate transparent surface for serialization
surface = Gfx::PaintingSurface::create_with_size(size, Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
}
auto const size = bitmap_size_for_canvas();
if (size.is_empty())
return nullptr;
RefPtr<Gfx::Bitmap> bitmap;
if (surface) {
bitmap = surface->snapshot_bitmap();
if (auto* webgl_context = this->webgl_context()) {
bitmap = webgl_context->context().read_back_drawing_buffer({ {}, size });
} else {
if (auto context = canvas_rendering_context_2d()) {
ensure_backing_storage();
if (auto pixels = context->read_pixels({ {}, size }); pixels && pixels->size() == size)
bitmap = pixels;
} else {
bitmap = create_transparent_canvas_bitmap(size);
}
}
return bitmap;
@ -431,7 +446,7 @@ void HTMLCanvasElement::set_canvas_content_dirty()
m_canvas_content_dirty = true;
}
void HTMLCanvasElement::present()
void HTMLCanvasElement::prepare_for_compositing()
{
if (!m_canvas_content_dirty)
return;
@ -439,80 +454,34 @@ void HTMLCanvasElement::present()
m_context.visit(
[](GC::Ref<CanvasRenderingContext2D>& context) {
context->present();
context->prepare_for_compositing();
},
[](GC::Ref<WebGL::WebGLRenderingContext>& context) {
context->present();
context->prepare_for_compositing();
},
[](GC::Ref<WebGL::WebGL2RenderingContext>& context) {
context->present();
},
[](Empty) {
// Do nothing.
});
update_compositor_surface();
}
void HTMLCanvasElement::republish_compositor_surface()
{
if (m_canvas_content_dirty) {
present();
return;
}
update_compositor_surface();
}
void HTMLCanvasElement::update_compositor_surface()
{
if (auto surface = this->surface()) {
surface->flush();
if (auto navigable = document().navigable(); navigable && navigable->has_compositor_context())
navigable->compositor_context().update_compositor_surface(ensure_compositor_surface_id(), surface->snapshot_into_shared_image());
}
}
void HTMLCanvasElement::clear_compositor_surface()
{
if (!m_compositor_surface_id.has_value())
return;
if (auto navigable = document().navigable(); navigable && navigable->has_compositor_context())
navigable->compositor_context().clear_compositor_surface(*m_compositor_surface_id);
}
RefPtr<Gfx::PaintingSurface> HTMLCanvasElement::surface() const
{
return m_context.visit(
[&](GC::Ref<CanvasRenderingContext2D> const& context) {
return context->surface();
},
[&](GC::Ref<WebGL::WebGLRenderingContext> const& context) -> RefPtr<Gfx::PaintingSurface> {
return context->surface();
},
[&](GC::Ref<WebGL::WebGL2RenderingContext> const& context) -> RefPtr<Gfx::PaintingSurface> {
return context->surface();
},
[](Empty) -> RefPtr<Gfx::PaintingSurface> {
return {};
});
}
void HTMLCanvasElement::allocate_painting_surface_if_needed()
{
m_context.visit(
[&](GC::Ref<CanvasRenderingContext2D>& context) {
context->allocate_painting_surface_if_needed();
},
[&](GC::Ref<WebGL::WebGLRenderingContext>& context) {
context->allocate_painting_surface_if_needed();
},
[&](GC::Ref<WebGL::WebGL2RenderingContext>& context) {
context->allocate_painting_surface_if_needed();
context->prepare_for_compositing();
},
[](Empty) {
// Do nothing.
});
}
Optional<Gfx::IntSize> HTMLCanvasElement::canvas_surface_content_size() const
{
if (!canvas_id().has_value())
return {};
auto size = bitmap_size_for_canvas();
if (size.is_empty())
return {};
return size;
}
void HTMLCanvasElement::ensure_backing_storage()
{
if (auto context = canvas_rendering_context_2d())
context->ensure_backing_storage();
}
}

View file

@ -8,7 +8,6 @@
#include <AK/Optional.h>
#include <LibGfx/Forward.h>
#include <LibGfx/PaintingSurface.h>
#include <LibWeb/HTML/HTMLElement.h>
#include <LibWeb/Painting/DisplayListResourceIds.h>
#include <LibWeb/WebIDL/Types.h>
@ -47,14 +46,20 @@ public:
WebIDL::ExceptionOr<void> to_blob(GC::Ref<WebIDL::CallbackType> callback, StringView type, Optional<JS::Value> quality);
RefPtr<Gfx::Bitmap> get_bitmap_from_surface();
void present();
void republish_compositor_surface();
void prepare_for_compositing();
void set_canvas_content_dirty();
GC::Ptr<HTML::CanvasRenderingContext2D> canvas_rendering_context_2d() const
{
if (auto const* context = m_context.get_pointer<GC::Ref<HTML::CanvasRenderingContext2D>>())
return *context;
return nullptr;
}
RefPtr<Gfx::PaintingSurface> surface() const;
void allocate_painting_surface_if_needed();
Optional<Painting::CanvasId> canvas_id() const;
Painting::CompositorSurfaceId ensure_compositor_surface_id();
Optional<Gfx::IntSize> canvas_surface_content_size() const;
void ensure_backing_storage();
CSS::ComputationContext canvas_font_computation_context();
@ -73,13 +78,11 @@ private:
template<typename ContextType>
JS::ThrowCompletionOr<HasOrCreatedContext> create_webgl_context(JS::Value options);
WebGL::WebGLRenderingContextBase* webgl_context() const;
void reset_context_to_default_state();
void notify_context_about_canvas_size_change();
void clear_compositor_surface();
void update_compositor_surface();
Variant<GC::Ref<HTML::CanvasRenderingContext2D>, GC::Ref<WebGL::WebGLRenderingContext>, GC::Ref<WebGL::WebGL2RenderingContext>, Empty> m_context;
Optional<Painting::CompositorSurfaceId> m_compositor_surface_id;
bool m_canvas_content_dirty { false };
};

View file

@ -8,6 +8,7 @@
#include <AK/NeverDestroyed.h>
#include <LibCore/Timer.h>
#include <LibGfx/PaintingSurface.h>
#include <LibWeb/CSS/ComputedProperties.h>
#include <LibWeb/CSS/PseudoElement.h>
#include <LibWeb/CSS/SystemColor.h>

View file

@ -142,7 +142,7 @@ WebIDL::ExceptionOr<GC::Ref<ImageData>> OffscreenCanvasRenderingContext2D::creat
return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::create_image_data(ImageData&)"_utf16);
}
WebIDL::ExceptionOr<GC::Ptr<ImageData>> OffscreenCanvasRenderingContext2D::get_image_data(int, int, int, int, Optional<Bindings::ImageDataSettings> const&) const
WebIDL::ExceptionOr<GC::Ptr<ImageData>> OffscreenCanvasRenderingContext2D::get_image_data(int, int, int, int, Optional<Bindings::ImageDataSettings> const&)
{
return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::get_image_data()"_utf16);
}

View file

@ -82,7 +82,7 @@ public:
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(int width, int height, Optional<Bindings::ImageDataSettings> const& settings = {}) const override;
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(ImageData const& image_data) const override;
virtual WebIDL::ExceptionOr<GC::Ptr<ImageData>> get_image_data(int x, int y, int width, int height, Optional<Bindings::ImageDataSettings> const& settings = {}) const override;
virtual WebIDL::ExceptionOr<GC::Ptr<ImageData>> get_image_data(int x, int y, int width, int height, Optional<Bindings::ImageDataSettings> const& settings = {}) override;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y) override;
virtual WebIDL::ExceptionOr<void> put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) override;

View file

@ -11,6 +11,7 @@
#include <AK/NumericLimits.h>
#include <AK/QuickSort.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/PaintingSurface.h>
#include <LibGfx/SkiaBackendContext.h>
#include <LibWeb/Bindings/MainThreadVM.h>
#include <LibWeb/DOM/Document.h>

View file

@ -716,17 +716,10 @@ void Page::for_each_canvas_element(Callback&& callback)
}
}
void Page::present_all_canvas_element_surfaces()
void Page::prepare_canvas_contexts_for_compositing()
{
for_each_canvas_element([](auto& canvas_element) {
canvas_element.present();
});
}
void Page::republish_all_canvas_element_surfaces()
{
for_each_canvas_element([](auto& canvas_element) {
canvas_element.republish_compositor_surface();
canvas_element.prepare_for_compositing();
});
}

View file

@ -233,8 +233,7 @@ public:
void register_canvas_element(Badge<HTML::HTMLCanvasElement>, UniqueNodeID canvas_id);
void unregister_canvas_element(Badge<HTML::HTMLCanvasElement>, UniqueNodeID canvas_id);
void present_all_canvas_element_surfaces();
void republish_all_canvas_element_surfaces();
void prepare_canvas_contexts_for_compositing();
struct MediaContextMenu {
URL::URL media_url;

View file

@ -32,13 +32,14 @@ void CanvasPaintable::paint(DisplayListRecordingContext& context, PaintPhase pha
ScopedCornerRadiusClip corner_clip { context, canvas_rect, normalized_border_radii_data(ShrinkRadiiForBorders::Yes) };
auto& canvas_element = as<HTML::HTMLCanvasElement>(*dom_node());
if (auto surface = canvas_element.surface()) {
if (auto content_size = canvas_element.canvas_surface_content_size(); content_size.has_value()) {
auto canvas_id = canvas_element.canvas_id();
VERIFY(canvas_id.has_value());
auto canvas_int_rect = canvas_rect.to_type<int>();
auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(),
surface->size(), canvas_int_rect.size());
auto& mutable_canvas_element = const_cast<HTML::HTMLCanvasElement&>(canvas_element);
context.display_list_recorder().draw_compositor_surface(canvas_int_rect,
mutable_canvas_element.ensure_compositor_surface_id(), scaling_mode);
*content_size, canvas_int_rect.size());
context.display_list_recorder().draw_canvas(canvas_int_rect,
*canvas_id, scaling_mode);
}
}
}

View file

@ -5,10 +5,13 @@
*/
#include <LibGfx/Bitmap.h>
#include <LibGfx/PaintingSurface.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/ElementFactory.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/CanvasRenderingContext2D.h>
#include <LibWeb/HTML/HTMLCanvasElement.h>
#include <LibWeb/HTML/ImageBitmap.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/TagNames.h>
#include <LibWeb/HTML/TraversableNavigable.h>
@ -46,9 +49,6 @@ ErrorOr<GC::Ref<HTML::HTMLCanvasElement>, WebDriver::Error> draw_bounding_box_fr
// FIXME: 5. Let context, a canvas context mode, be the result of invoking the 2D context creation algorithm given canvas as the target.
MUST(canvas.create_2d_context({}));
canvas.allocate_painting_surface_if_needed();
if (!canvas.surface())
return Error::from_code(ErrorCode::UnableToCaptureScreen, "Failed to allocate painting surface"sv);
// 6. Complete implementation specific steps equivalent to drawing the region of the framebuffer specified by the following coordinates onto context:
// - X coordinate: rectangle x coordinate
@ -57,7 +57,7 @@ ErrorOr<GC::Ref<HTML::HTMLCanvasElement>, WebDriver::Error> draw_bounding_box_fr
// - Height: paint height
Gfx::IntRect paint_rect { rect.x(), rect.y(), paint_width, paint_height };
auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, canvas.surface()->size()));
auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, Gfx::IntSize { paint_width, paint_height }));
auto painting_surface = Gfx::PaintingSurface::wrap_bitmap(bitmap);
IGNORE_USE_IN_ESCAPING_LAMBDA bool did_paint = false;
HTML::PaintConfig paint_config { .canvas_fill_rect = paint_rect };
@ -68,7 +68,10 @@ ErrorOr<GC::Ref<HTML::HTMLCanvasElement>, WebDriver::Error> draw_bounding_box_fr
return did_paint;
}));
canvas.surface()->write_from_bitmap(*bitmap);
auto image_bitmap = HTML::ImageBitmap::create(element.realm());
image_bitmap->set_bitmap(bitmap);
if (canvas.canvas_rendering_context_2d()->draw_image(image_bitmap, 0, 0).is_exception())
return Error::from_code(ErrorCode::UnableToCaptureScreen, "Failed to draw the screenshot to the canvas"sv);
// 7. Return success with canvas.
return canvas;
@ -80,7 +83,7 @@ Response encode_canvas_element(HTML::HTMLCanvasElement& canvas)
// FIXME: 1. If the canvas elements bitmaps origin-clean flag is set to false, return error with error code unable to capture screen.
// 2. If the canvas elements bitmap has no pixels (i.e. either its horizontal dimension or vertical dimension is zero) then return error with error code unable to capture screen.
if (canvas.surface()->size().is_empty())
if (!canvas.canvas_surface_content_size().has_value())
return Error::from_code(ErrorCode::UnableToCaptureScreen, "Captured screenshot is empty"sv);
// 3. Let file be a serialization of the canvas elements bitmap as a file, using "image/png" as an argument.

View file

@ -8,7 +8,7 @@
#include <LibWeb/Bindings/ANGLEInstancedArrays.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/WebGL/Extensions/ANGLEInstancedArrays.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
#include <GLES2/gl2.h>

View file

@ -8,7 +8,7 @@
#include <LibWeb/Bindings/EXTBlendMinMax.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/WebGL/Extensions/EXTBlendMinMax.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
namespace Web::WebGL {

View file

@ -8,7 +8,7 @@
#include <LibWeb/Bindings/EXTColorBufferFloat.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/WebGL/Extensions/EXTColorBufferFloat.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
namespace Web::WebGL {

View file

@ -8,7 +8,7 @@
#include <LibWeb/Bindings/EXTRenderSnorm.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/WebGL/Extensions/EXTRenderSnorm.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
namespace Web::WebGL {

View file

@ -8,7 +8,7 @@
#include <LibWeb/Bindings/EXTTextureFilterAnisotropic.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/WebGL/Extensions/EXTTextureFilterAnisotropic.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
namespace Web::WebGL {

View file

@ -8,7 +8,7 @@
#include <LibWeb/Bindings/EXTTextureNorm16.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/WebGL/Extensions/EXTTextureNorm16.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
namespace Web::WebGL {

View file

@ -8,7 +8,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/OESElementIndexUint.h>
#include <LibWeb/WebGL/Extensions/OESElementIndexUint.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
namespace Web::WebGL {

View file

@ -8,7 +8,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/OESStandardDerivatives.h>
#include <LibWeb/WebGL/Extensions/OESStandardDerivatives.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
namespace Web::WebGL {

View file

@ -9,7 +9,7 @@
#include <LibWeb/Bindings/OESVertexArrayObject.h>
#include <LibWeb/WebGL/Extensions/OESVertexArrayObject.h>
#include <LibWeb/WebGL/Extensions/WebGLVertexArrayObjectOES.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
#include <GLES2/gl2.h>
@ -43,33 +43,38 @@ void OESVertexArrayObject::delete_vertex_array_oes(GC::Ptr<WebGLVertexArrayObjec
{
m_context->context().make_current();
GLuint vertex_array_handle = 0;
if (array_object) {
auto handle_or_error = array_object->handle(m_context.ptr());
if (handle_or_error.is_error()) {
// FIXME: m_context->set_error(GL_INVALID_OPERATION);
return;
}
vertex_array_handle = handle_or_error.release_value();
}
if (!array_object)
return;
m_context->context().delete_vertex_arrays_oes(1, &vertex_array_handle);
auto handle_or_error = array_object->handle_for_deletion(m_context.ptr());
if (handle_or_error.is_error()) {
// FIXME: m_context->set_error(GL_INVALID_OPERATION);
return;
}
auto vertex_array_handle = handle_or_error.release_value();
if (!vertex_array_handle.has_value())
return;
auto handle = vertex_array_handle.value();
m_context->context().delete_vertex_arrays_oes(1, &handle);
}
bool OESVertexArrayObject::is_vertex_array_oes(GC::Ptr<WebGLVertexArrayObjectOES> array_object)
{
m_context->context().make_current();
GLuint vertex_array_handle = 0;
if (array_object) {
auto handle_or_error = array_object->handle(m_context.ptr());
if (handle_or_error.is_error()) {
return false;
}
vertex_array_handle = handle_or_error.release_value();
}
if (!array_object)
return false;
return m_context->context().is_vertex_array_oes(vertex_array_handle) == GL_TRUE;
auto handle_or_error = array_object->handle_for_query(m_context.ptr());
if (handle_or_error.is_error()) {
return false;
}
auto vertex_array_handle = handle_or_error.release_value();
if (!vertex_array_handle.has_value())
return false;
return m_context->context().is_vertex_array_oes(vertex_array_handle.value()) == GL_TRUE;
}
void OESVertexArrayObject::bind_vertex_array_oes(GC::Ptr<WebGLVertexArrayObjectOES> array_object)

View file

@ -11,7 +11,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/WebGLCompressedTextureS3tc.h>
#include <LibWeb/WebGL/Extensions/WebGLCompressedTextureS3tc.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
namespace Web::WebGL {

View file

@ -11,7 +11,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/WebGLCompressedTextureS3tcSrgb.h>
#include <LibWeb/WebGL/Extensions/WebGLCompressedTextureS3tcSrgb.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
namespace Web::WebGL {

View file

@ -8,7 +8,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/WebGLDrawBuffers.h>
#include <LibWeb/WebGL/Extensions/WebGLDrawBuffers.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
#include <GLES2/gl2.h>

View file

@ -37,6 +37,56 @@ extern "C" {
namespace Web::WebGL {
Optional<Gfx::ExportFormat> texture_export_format(GLenum format, GLenum type)
{
switch (format) {
case GL_RGB:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::RGB888;
case GL_UNSIGNED_SHORT_5_6_5:
return Gfx::ExportFormat::RGB565;
default:
break;
}
break;
case GL_RGBA:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::RGBA8888;
case GL_UNSIGNED_SHORT_4_4_4_4:
// FIXME: This is not exactly the same as RGBA.
return Gfx::ExportFormat::RGBA4444;
case GL_UNSIGNED_SHORT_5_5_5_1:
return Gfx::ExportFormat::RGBA5551;
default:
break;
}
break;
case GL_ALPHA:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::Alpha8;
default:
break;
}
break;
case GL_LUMINANCE:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::Gray8;
default:
break;
}
break;
default:
break;
}
dbgln("WebGL: Unsupported format and type combination. format: 0x{:04x}, type: 0x{:04x}", format, type);
return {};
}
struct OpenGLContext::Impl {
EGLDisplay display { EGL_NO_DISPLAY };
EGLConfig config { EGL_NO_CONFIG_KHR };
@ -525,14 +575,4 @@ Vector<String> OpenGLContext::get_supported_opengl_extensions()
#endif
}
void OpenGLContext::request_extension(char const* extension_name)
{
#ifdef ENABLE_WEBGL
make_current();
glRequestExtensionANGLE(extension_name);
#else
(void)extension_name;
#endif
}
}

View file

@ -11,6 +11,7 @@
#include <AK/OwnPtr.h>
#include <AK/RefPtr.h>
#include <AK/Vector.h>
#include <LibGfx/BitmapExport.h>
#include <LibGfx/Forward.h>
#include <LibGfx/Size.h>
#include <LibWeb/Export.h>
@ -18,12 +19,11 @@
namespace Web::WebGL {
WEB_API Optional<Gfx::ExportFormat> texture_export_format(GLenum format, GLenum type);
class WEB_API OpenGLContext : public GLFunctions {
public:
enum class WebGLVersion {
WebGL1,
WebGL2,
};
using WebGLVersion = WebGL::WebGLVersion;
struct DrawingBufferOptions {
bool depth;
@ -54,9 +54,6 @@ public:
u32 default_renderbuffer() const;
Vector<String> get_supported_opengl_extensions();
void request_extension(char const* extension_name);
WebGLVersion webgl_version() const { return m_webgl_version; }
private:
NonnullRefPtr<Gfx::SkiaBackendContext> m_skia_backend_context;

View file

@ -7,6 +7,7 @@
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/Optional.h>
#include <AK/RefCounted.h>
#include <AK/String.h>
#include <AK/Vector.h>
@ -27,19 +28,19 @@ public:
struct CreateResult {
bool success { false };
Painting::CanvasId canvas_id { 0 };
Vector<String> supported_extensions;
};
virtual CreateResult create_context(WebGLVersion, Gfx::IntSize initial_size, bool depth, bool stencil, bool antialias) = 0;
virtual void destroy_context(Painting::CanvasId) = 0;
virtual Optional<Painting::CanvasId> canvas_id() const = 0;
virtual void destroy_context() = 0;
virtual void send_commands(Painting::CanvasId, ByteBuffer const&, Vector<Gfx::DecodedImageFrame> const& bitmaps) = 0;
virtual void present_canvas(Painting::CanvasId, bool preserve_drawing_buffer) = 0;
virtual ByteBuffer sync_call(Painting::CanvasId, ByteBuffer request) = 0;
virtual ReadPixelsResult read_pixels_robust_angle(Painting::CanvasId, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei buf_size, Core::AnonymousBuffer pixels) = 0;
virtual void read_buffer_sub_data(Painting::CanvasId, GLenum target, GLintptr offset, GLintptr size, Core::AnonymousBuffer data) = 0;
virtual void send_commands(ByteBuffer const&, Vector<Gfx::DecodedImageFrame> const& bitmaps) = 0;
virtual void present_canvas(bool preserve_drawing_buffer) = 0;
virtual ByteBuffer sync_call(ByteBuffer request) = 0;
virtual ReadPixelsResult read_pixels_robust_angle(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei buf_size, Core::AnonymousBuffer pixels) = 0;
virtual void read_buffer_sub_data(GLenum target, GLintptr offset, GLintptr size, Core::AnonymousBuffer data) = 0;
virtual Gfx::ShareableBitmap read_back_drawing_buffer(Painting::CanvasId, Gfx::IntRect const&) = 0;
virtual Gfx::ShareableBitmap read_back_drawing_buffer(Gfx::IntRect const&) = 0;
};
}

View file

@ -6,7 +6,6 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/SkiaBackendContext.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibWeb/Bindings/Intrinsics.h>
@ -15,9 +14,9 @@
#include <LibWeb/Infra/Strings.h>
#include <LibWeb/Painting/Paintable.h>
#include <LibWeb/WebGL/EventNames.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGL2RenderingContext.h>
#include <LibWeb/WebGL/WebGLContextEvent.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContext.h>
#include <LibWeb/WebGL/WebGLShader.h>
#include <LibWeb/WebIDL/Buffers.h>
@ -34,28 +33,16 @@ JS::ThrowCompletionOr<GC::Ptr<WebGL2RenderingContext>> WebGL2RenderingContext::c
// We should be coming here from getContext being called on a wrapped <canvas> element.
auto context_attributes = TRY(convert_value_to_context_attributes_dictionary(canvas_element.vm(), options));
auto skia_backend_context = Gfx::SkiaBackendContext::the_main_thread_context();
if (!skia_backend_context) {
fire_webgl_context_creation_error(canvas_element);
return GC::Ptr<WebGL2RenderingContext> { nullptr };
}
OpenGLContext::DrawingBufferOptions context_options {
.depth = context_attributes.depth,
.stencil = context_attributes.stencil,
.antialias = context_attributes.antialias,
};
auto context = OpenGLContext::create(*skia_backend_context, OpenGLContext::WebGLVersion::WebGL2, context_options);
auto context = create_webgl_context_proxy(canvas_element, WebGLVersion::WebGL2, context_attributes);
if (!context) {
fire_webgl_context_creation_error(canvas_element);
return GC::Ptr<WebGL2RenderingContext> { nullptr };
}
context->set_size(canvas_element.bitmap_size_for_canvas(1, 1));
return realm.create<WebGL2RenderingContext>(realm, canvas_element, context.release_nonnull(), context_attributes, context_attributes);
}
WebGL2RenderingContext::WebGL2RenderingContext(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr<OpenGLContext> context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters)
WebGL2RenderingContext::WebGL2RenderingContext(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr<WebGLContextProxy> context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters)
: WebGL2RenderingContextOverloads(realm, move(context))
, m_canvas_element(canvas_element)
, m_context_creation_parameters(context_creation_parameters)
@ -78,9 +65,9 @@ void WebGL2RenderingContext::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_canvas_element);
}
void WebGL2RenderingContext::present()
void WebGL2RenderingContext::prepare_for_compositing()
{
context().present(m_context_creation_parameters.preserve_drawing_buffer);
context().present_canvas_for_compositing(m_context_creation_parameters.preserve_drawing_buffer);
}
GC::Ref<HTML::HTMLCanvasElement> WebGL2RenderingContext::canvas_for_binding() const
@ -88,7 +75,7 @@ GC::Ref<HTML::HTMLCanvasElement> WebGL2RenderingContext::canvas_for_binding() co
return *m_canvas_element;
}
void WebGL2RenderingContext::needs_to_present()
void WebGL2RenderingContext::did_update_canvas_content()
{
m_canvas_element->set_canvas_content_dirty();
@ -105,8 +92,8 @@ Optional<WebGLContextAttributes> WebGL2RenderingContext::get_context_attributes(
void WebGL2RenderingContext::set_size(Gfx::IntSize const& size)
{
Gfx::IntSize final_size;
final_size.set_width(max(size.width(), 1));
final_size.set_height(max(size.height(), 1));
final_size.set_width(clamp(size.width(), 1, max_webgl_drawing_buffer_dimension));
final_size.set_height(clamp(size.height(), 1, max_webgl_drawing_buffer_dimension));
context().set_size(final_size);
}
@ -114,26 +101,16 @@ void WebGL2RenderingContext::reset_to_default_state()
{
}
RefPtr<Gfx::PaintingSurface> WebGL2RenderingContext::surface()
{
return context().surface();
}
void WebGL2RenderingContext::allocate_painting_surface_if_needed()
{
context().allocate_painting_surface_if_needed();
}
WebIDL::Long WebGL2RenderingContext::drawing_buffer_width() const
{
auto size = canvas_for_binding()->bitmap_size_for_canvas();
return size.width();
return min(size.width(), max_webgl_drawing_buffer_dimension);
}
WebIDL::Long WebGL2RenderingContext::drawing_buffer_height() const
{
auto size = canvas_for_binding()->bitmap_size_for_canvas();
return size.height();
return min(size.height(), max_webgl_drawing_buffer_dimension);
}
}

View file

@ -26,16 +26,13 @@ public:
virtual ~WebGL2RenderingContext() override;
void present() override;
void needs_to_present() override;
void prepare_for_compositing() override;
void did_update_canvas_content() override;
GC::Ref<HTML::HTMLCanvasElement> canvas_for_binding() const;
virtual GC::Ref<HTML::HTMLCanvasElement> canvas_for_binding() const override;
Optional<WebGLContextAttributes> get_context_attributes();
RefPtr<Gfx::PaintingSurface> surface();
void allocate_painting_surface_if_needed();
void set_size(Gfx::IntSize const&);
void reset_to_default_state();
@ -45,7 +42,7 @@ public:
private:
virtual void initialize(JS::Realm&) override;
WebGL2RenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr<OpenGLContext> context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters);
WebGL2RenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr<WebGLContextProxy> context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters);
virtual void visit_edges(Cell::Visitor&) override;

View file

@ -16,10 +16,10 @@ extern "C" {
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGL2RenderingContextImpl.h>
#include <LibWeb/WebGL/WebGLActiveInfo.h>
#include <LibWeb/WebGL/WebGLBuffer.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLFramebuffer.h>
#include <LibWeb/WebGL/WebGLProgram.h>
#include <LibWeb/WebGL/WebGLQuery.h>
@ -36,7 +36,7 @@ extern "C" {
namespace Web::WebGL {
WebGL2RenderingContextImpl::WebGL2RenderingContextImpl(JS::Realm& realm, NonnullOwnPtr<OpenGLContext> context)
WebGL2RenderingContextImpl::WebGL2RenderingContextImpl(JS::Realm& realm, NonnullOwnPtr<WebGLContextProxy> context)
: WebGLRenderingContextImpl(realm, move(context))
{
}
@ -89,21 +89,17 @@ void WebGL2RenderingContextImpl::get_buffer_sub_data(WebIDL::UnsignedLong target
// If copyLength is greater than zero, copy copyLength typed elements (each of size elementSize) from buf into
// dstBuffer, reading buf starting at byte index srcByteOffset and writing into dstBuffer starting at element
// index dstOffset.
auto* buffer_data = m_context->map_buffer_range(target, src_byte_offset, copy_bytes, GL_MAP_READ_BIT);
if (!buffer_data)
return;
dst_buffer.write({ buffer_data, copy_bytes }, dst_offset_in_bytes);
m_context->unmap_buffer(target);
// index dstOffset. The destination span is bounds-checked above, so the readback can
// land directly in the JS-owned buffer without staging.
auto dst_span = dst_buffer.viewed_array_buffer()->span().slice(dst_buffer.byte_offset() + dst_offset_in_bytes, copy_bytes);
m_context->read_buffer_sub_data(target, src_byte_offset, dst_span);
}
void WebGL2RenderingContextImpl::blit_framebuffer(WebIDL::Long src_x0, WebIDL::Long src_y0, WebIDL::Long src_x1, WebIDL::Long src_y1, WebIDL::Long dst_x0, WebIDL::Long dst_y0, WebIDL::Long dst_x1, WebIDL::Long dst_y1, WebIDL::UnsignedLong mask, WebIDL::UnsignedLong filter)
{
m_context->make_current();
m_context->notify_content_will_change();
needs_to_present();
did_update_canvas_content();
m_context->blit_framebuffer(src_x0, src_y0, src_x1, src_y1, dst_x0, dst_y0, dst_x1, dst_y1, mask, filter);
}
@ -130,7 +126,7 @@ void WebGL2RenderingContextImpl::invalidate_framebuffer(WebIDL::UnsignedLong tar
m_context->notify_content_will_change();
m_context->invalidate_framebuffer(target, attachments.size(), attachments.data());
needs_to_present();
did_update_canvas_content();
}
void WebGL2RenderingContextImpl::invalidate_sub_framebuffer(WebIDL::UnsignedLong target, Vector<WebIDL::UnsignedLong> attachments, WebIDL::Long x, WebIDL::Long y, WebIDL::Long width, WebIDL::Long height)
@ -139,7 +135,7 @@ void WebGL2RenderingContextImpl::invalidate_sub_framebuffer(WebIDL::UnsignedLong
m_context->notify_content_will_change();
m_context->invalidate_sub_framebuffer(target, attachments.size(), attachments.data(), x, y, width, height);
needs_to_present();
did_update_canvas_content();
}
void WebGL2RenderingContextImpl::read_buffer(WebIDL::UnsignedLong src)
@ -156,9 +152,8 @@ JS::Value WebGL2RenderingContextImpl::get_internalformat_parameter(WebIDL::Unsig
case GL_SAMPLES: {
GLint num_sample_counts { 0 };
m_context->get_internalformativ_robust_angle(target, internalformat, GL_NUM_SAMPLE_COUNTS, 1, nullptr, &num_sample_counts);
size_t buffer_size = num_sample_counts * sizeof(GLint);
auto samples_buffer = MUST(ByteBuffer::create_zeroed(buffer_size));
m_context->get_internalformativ_robust_angle(target, internalformat, GL_SAMPLES, buffer_size, nullptr, reinterpret_cast<GLint*>(samples_buffer.data()));
auto samples_buffer = MUST(ByteBuffer::create_zeroed(num_sample_counts * sizeof(GLint)));
m_context->get_internalformativ_robust_angle(target, internalformat, GL_SAMPLES, num_sample_counts, nullptr, reinterpret_cast<GLint*>(samples_buffer.data()));
auto array_buffer = JS::ArrayBuffer::create(realm(), move(samples_buffer));
return JS::Int32Array::create(realm(), num_sample_counts, array_buffer);
}
@ -488,7 +483,7 @@ void WebGL2RenderingContextImpl::draw_arrays_instanced(WebIDL::UnsignedLong mode
{
m_context->make_current();
m_context->notify_content_will_change();
needs_to_present();
did_update_canvas_content();
m_context->draw_arrays_instanced(mode, first, count, instance_count);
}
@ -498,14 +493,14 @@ void WebGL2RenderingContextImpl::draw_elements_instanced(WebIDL::UnsignedLong mo
m_context->notify_content_will_change();
m_context->draw_elements_instanced(mode, count, type, reinterpret_cast<void*>(offset), instance_count);
needs_to_present();
did_update_canvas_content();
}
void WebGL2RenderingContextImpl::draw_range_elements(WebIDL::UnsignedLong mode, WebIDL::UnsignedLong start, WebIDL::UnsignedLong end, WebIDL::Long count, WebIDL::UnsignedLong type, WebIDL::LongLong offset)
{
m_context->make_current();
m_context->notify_content_will_change();
needs_to_present();
did_update_canvas_content();
m_context->draw_range_elements(mode, start, end, count, type, reinterpret_cast<void*>(offset));
}
@ -544,7 +539,7 @@ void WebGL2RenderingContextImpl::clear_bufferfv(WebIDL::UnsignedLong buffer, Web
}
m_context->clear_bufferfv(buffer, drawbuffer, span.data());
needs_to_present();
did_update_canvas_content();
}
void WebGL2RenderingContextImpl::clear_bufferiv(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, Int32List values, WebIDL::UnsignedLongLong src_offset)
@ -575,7 +570,7 @@ void WebGL2RenderingContextImpl::clear_bufferiv(WebIDL::UnsignedLong buffer, Web
}
m_context->clear_bufferiv(buffer, drawbuffer, span.data());
needs_to_present();
did_update_canvas_content();
}
void WebGL2RenderingContextImpl::clear_bufferuiv(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, Uint32List values, WebIDL::UnsignedLongLong src_offset)
@ -605,14 +600,14 @@ void WebGL2RenderingContextImpl::clear_bufferuiv(WebIDL::UnsignedLong buffer, We
}
m_context->clear_bufferuiv(buffer, drawbuffer, span.data());
needs_to_present();
did_update_canvas_content();
}
void WebGL2RenderingContextImpl::clear_bufferfi(WebIDL::UnsignedLong buffer, WebIDL::Long drawbuffer, float depth, WebIDL::Long stencil)
{
m_context->make_current();
m_context->notify_content_will_change();
needs_to_present();
did_update_canvas_content();
m_context->clear_bufferfi(buffer, drawbuffer, depth, stencil);
}
@ -629,17 +624,20 @@ void WebGL2RenderingContextImpl::delete_query(GC::Ptr<WebGLQuery> query)
{
m_context->make_current();
GLuint query_handle = 0;
if (query) {
auto handle_or_error = query->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
query_handle = handle_or_error.release_value();
}
if (!query)
return;
m_context->delete_queries(1, &query_handle);
auto handle_or_error = query->handle_for_deletion(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
auto query_handle = handle_or_error.release_value();
if (!query_handle.has_value())
return;
auto handle = query_handle.value();
m_context->delete_queries(1, &handle);
}
void WebGL2RenderingContextImpl::begin_query(WebIDL::UnsignedLong target, GC::Ref<WebGLQuery> query)
@ -744,17 +742,20 @@ void WebGL2RenderingContextImpl::delete_sampler(GC::Ptr<WebGLSampler> sampler)
{
m_context->make_current();
GLuint sampler_handle = 0;
if (sampler) {
auto handle_or_error = sampler->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
sampler_handle = handle_or_error.release_value();
}
if (!sampler)
return;
m_context->delete_samplers(1, &sampler_handle);
auto handle_or_error = sampler->handle_for_deletion(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
auto sampler_handle = handle_or_error.release_value();
if (!sampler_handle.has_value())
return;
auto handle = sampler_handle.value();
m_context->delete_samplers(1, &handle);
}
void WebGL2RenderingContextImpl::bind_sampler(WebIDL::UnsignedLong unit, GC::Ptr<WebGLSampler> sampler)
@ -859,17 +860,19 @@ void WebGL2RenderingContextImpl::delete_sync(GC::Ptr<WebGLSync> sync)
{
m_context->make_current();
GLsync sync_handle = nullptr;
if (sync) {
auto handle_or_error = sync->sync_handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
sync_handle = static_cast<GLsync>(handle_or_error.release_value());
}
if (!sync)
return;
m_context->delete_sync(sync_handle);
auto handle_or_error = sync->sync_handle_for_deletion(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
auto sync_handle = handle_or_error.release_value();
if (!sync_handle.has_value())
return;
m_context->delete_sync(static_cast<GLsync>(sync_handle.value()));
}
WebIDL::UnsignedLong WebGL2RenderingContextImpl::client_wait_sync(GC::Ref<WebGLSync> sync, WebIDL::UnsignedLong flags, WebIDL::UnsignedLongLong timeout)
@ -929,17 +932,20 @@ void WebGL2RenderingContextImpl::delete_transform_feedback(GC::Ptr<WebGLTransfor
{
m_context->make_current();
GLuint transform_feedback_handle = 0;
if (transform_feedback) {
auto handle_or_error = transform_feedback->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
transform_feedback_handle = handle_or_error.release_value();
}
if (!transform_feedback)
return;
m_context->delete_transform_feedbacks(1, &transform_feedback_handle);
auto handle_or_error = transform_feedback->handle_for_deletion(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
auto transform_feedback_handle = handle_or_error.release_value();
if (!transform_feedback_handle.has_value())
return;
auto handle = transform_feedback_handle.value();
m_context->delete_transform_feedbacks(1, &handle);
}
void WebGL2RenderingContextImpl::bind_transform_feedback(WebIDL::UnsignedLong target, GC::Ptr<WebGLTransformFeedback> transform_feedback)
@ -1161,7 +1167,7 @@ JS::Value WebGL2RenderingContextImpl::get_active_uniform_block_parameter(GC::Ref
}
case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES: {
GLint num_active_uniforms = 0;
m_context->get_active_uniform_blockiv_robust_angle(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, sizeof(GLint), nullptr, &num_active_uniforms);
m_context->get_active_uniform_blockiv_robust_angle(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, 1, nullptr, &num_active_uniforms);
size_t buffer_size = num_active_uniforms * sizeof(GLint);
auto active_uniform_indices_buffer = MUST(ByteBuffer::create_zeroed(buffer_size));
m_context->get_active_uniform_blockiv_robust_angle(program_handle, uniform_block_index, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, num_active_uniforms, nullptr, reinterpret_cast<GLint*>(active_uniform_indices_buffer.data()));
@ -1228,17 +1234,20 @@ void WebGL2RenderingContextImpl::delete_vertex_array(GC::Ptr<WebGLVertexArrayObj
{
m_context->make_current();
GLuint vertex_array_handle = 0;
if (vertex_array) {
auto handle_or_error = vertex_array->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
vertex_array_handle = handle_or_error.release_value();
}
if (!vertex_array)
return;
m_context->delete_vertex_arrays(1, &vertex_array_handle);
auto handle_or_error = vertex_array->handle_for_deletion(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
auto vertex_array_handle = handle_or_error.release_value();
if (!vertex_array_handle.has_value())
return;
auto handle = vertex_array_handle.value();
m_context->delete_vertex_arrays(1, &handle);
if (m_current_vertex_array == vertex_array)
m_current_vertex_array = nullptr;
}
@ -1247,16 +1256,18 @@ bool WebGL2RenderingContextImpl::is_vertex_array(GC::Ptr<WebGLVertexArrayObject>
{
m_context->make_current();
auto vertex_array_handle = 0;
if (vertex_array) {
auto handle_or_error = vertex_array->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
vertex_array_handle = handle_or_error.release_value();
if (!vertex_array)
return false;
auto handle_or_error = vertex_array->handle_for_query(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
return m_context->is_vertex_array(vertex_array_handle);
auto vertex_array_handle = handle_or_error.release_value();
if (!vertex_array_handle.has_value())
return false;
return m_context->is_vertex_array(vertex_array_handle.value());
}
void WebGL2RenderingContextImpl::bind_vertex_array(GC::Ptr<WebGLVertexArrayObject> array)

View file

@ -24,7 +24,7 @@ class WebGL2RenderingContextImpl : public WebGLRenderingContextImpl {
WEB_NON_IDL_PLATFORM_OBJECT(WebGL2RenderingContextImpl, WebGLRenderingContextImpl);
public:
WebGL2RenderingContextImpl(JS::Realm&, NonnullOwnPtr<OpenGLContext>);
WebGL2RenderingContextImpl(JS::Realm&, NonnullOwnPtr<WebGLContextProxy>);
void copy_buffer_sub_data(WebIDL::UnsignedLong read_target, WebIDL::UnsignedLong write_target, WebIDL::LongLong read_offset, WebIDL::LongLong write_offset, WebIDL::LongLong size);
void get_buffer_sub_data(WebIDL::UnsignedLong target, WebIDL::LongLong src_byte_offset, WebIDL::ArrayBufferView dst_buffer, WebIDL::UnsignedLongLong dst_offset, WebIDL::UnsignedLong length);

View file

@ -16,14 +16,14 @@ extern "C" {
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGL2RenderingContextOverloads.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLUniformLocation.h>
#include <LibWeb/WebIDL/Buffers.h>
namespace Web::WebGL {
WebGL2RenderingContextOverloads::WebGL2RenderingContextOverloads(JS::Realm& realm, NonnullOwnPtr<OpenGLContext> context)
WebGL2RenderingContextOverloads::WebGL2RenderingContextOverloads(JS::Realm& realm, NonnullOwnPtr<WebGLContextProxy> context)
: WebGL2RenderingContextImpl(realm, move(context))
{
}
@ -90,11 +90,11 @@ void WebGL2RenderingContextOverloads::tex_image2d(WebIDL::UnsignedLong target, W
{
m_context->make_current();
auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type);
if (!maybe_converted_texture.has_value())
auto maybe_source_frame = read_texture_image_source(source, format, type);
if (!maybe_source_frame.has_value())
return;
auto converted_texture = maybe_converted_texture.release_value();
m_context->tex_image2d_robust_angle(target, level, internalformat, converted_texture.width, converted_texture.height, 0, format, type, converted_texture.buffer.size(), converted_texture.buffer.data());
auto source_frame = maybe_source_frame.release_value();
m_context->tex_image2d_from_bitmap(target, level, internalformat, format, type, move(source_frame.frame), OptionalNone {}, source_frame.flip_y, source_frame.premultiply_alpha);
}
void WebGL2RenderingContextOverloads::tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, WebIDL::NullableArrayBufferViewVariant pixels)
@ -113,23 +113,29 @@ void WebGL2RenderingContextOverloads::tex_sub_image2d(WebIDL::UnsignedLong targe
{
m_context->make_current();
auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type);
if (!maybe_converted_texture.has_value())
auto maybe_source_frame = read_texture_image_source(source, format, type);
if (!maybe_source_frame.has_value())
return;
auto converted_texture = maybe_converted_texture.release_value();
m_context->tex_sub_image2d_robust_angle(target, level, xoffset, yoffset, converted_texture.width, converted_texture.height, format, type, converted_texture.buffer.size(), converted_texture.buffer.data());
auto source_frame = maybe_source_frame.release_value();
m_context->tex_sub_image2d_from_bitmap(target, level, xoffset, yoffset, format, type, move(source_frame.frame), OptionalNone {}, source_frame.flip_y, source_frame.premultiply_alpha);
}
void WebGL2RenderingContextOverloads::tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, TexImageSource source)
{
m_context->make_current();
auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type, width, height);
if (!maybe_converted_texture.has_value())
// https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml
// border: This value must be 0.
if (border != 0) {
set_error(GL_INVALID_VALUE);
return;
auto converted_texture = maybe_converted_texture.release_value();
m_context->tex_image2d_robust_angle(target, level, internalformat, converted_texture.width, converted_texture.height, border, format, type, converted_texture.buffer.size(), converted_texture.buffer.data());
}
auto maybe_source_frame = read_texture_image_source(source, format, type);
if (!maybe_source_frame.has_value())
return;
auto source_frame = maybe_source_frame.release_value();
m_context->tex_image2d_from_bitmap(target, level, internalformat, format, type, move(source_frame.frame), Gfx::IntSize { width, height }, source_frame.flip_y, source_frame.premultiply_alpha);
}
void WebGL2RenderingContextOverloads::tex_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long internalformat, WebIDL::Long width, WebIDL::Long height, WebIDL::Long border, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, WebIDL::ArrayBufferView src_data, WebIDL::UnsignedLongLong src_offset)
@ -145,12 +151,11 @@ void WebGL2RenderingContextOverloads::tex_sub_image2d(WebIDL::UnsignedLong targe
{
m_context->make_current();
auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type, width, height);
if (!maybe_converted_texture.has_value())
auto maybe_source_frame = read_texture_image_source(source, format, type);
if (!maybe_source_frame.has_value())
return;
auto converted_texture = maybe_converted_texture.release_value();
m_context->tex_sub_image2d_robust_angle(target, level, xoffset, yoffset, converted_texture.width, converted_texture.height, format, type, converted_texture.buffer.size(), converted_texture.buffer.data());
auto source_frame = maybe_source_frame.release_value();
m_context->tex_sub_image2d_from_bitmap(target, level, xoffset, yoffset, format, type, move(source_frame.frame), Gfx::IntSize { width, height }, source_frame.flip_y, source_frame.premultiply_alpha);
}
void WebGL2RenderingContextOverloads::tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, WebIDL::ArrayBufferView src_data, WebIDL::UnsignedLongLong src_offset)
@ -393,7 +398,7 @@ void WebGL2RenderingContextOverloads::read_pixels(WebIDL::Long x, WebIDL::Long y
return;
}
m_context->read_pixels_robust_angle(x, y, width, height, format, type, 0, nullptr, nullptr, nullptr, reinterpret_cast<void*>(offset));
m_context->read_pixels_into_pixel_pack_buffer(x, y, width, height, format, type, offset);
}
}

View file

@ -23,7 +23,7 @@ class WebGL2RenderingContextOverloads : public WebGL2RenderingContextImpl {
WEB_NON_IDL_PLATFORM_OBJECT(WebGL2RenderingContextOverloads, WebGL2RenderingContextImpl);
public:
WebGL2RenderingContextOverloads(JS::Realm&, NonnullOwnPtr<OpenGLContext>);
WebGL2RenderingContextOverloads(JS::Realm&, NonnullOwnPtr<WebGLContextProxy>);
void buffer_data(WebIDL::UnsignedLong target, WebIDL::LongLong size, WebIDL::UnsignedLong usage);
void buffer_data(WebIDL::UnsignedLong target, WebIDL::NullableBufferSourceVariant src_data, WebIDL::UnsignedLong usage);

View file

@ -13,9 +13,8 @@
namespace Web::WebGL {
WebGLContextProxyBase::WebGLContextProxyBase(NonnullRefPtr<RemoteWebGLTransport> transport, Painting::CanvasId canvas_id, WebGLVersion webgl_version, Vector<String> supported_extensions)
WebGLContextProxyBase::WebGLContextProxyBase(NonnullRefPtr<RemoteWebGLTransport> transport, WebGLVersion webgl_version, Vector<String> supported_extensions)
: m_transport(move(transport))
, m_canvas_id(canvas_id)
, m_webgl_version(webgl_version)
, m_supported_extensions(move(supported_extensions))
{
@ -23,14 +22,14 @@ WebGLContextProxyBase::WebGLContextProxyBase(NonnullRefPtr<RemoteWebGLTransport>
WebGLContextProxyBase::~WebGLContextProxyBase()
{
m_transport->destroy_context(m_canvas_id);
m_transport->destroy_context();
}
void WebGLContextProxyBase::flush_commands()
{
if (m_commands.is_empty())
return;
m_transport->send_commands(m_canvas_id, m_commands.buffer(), m_pending_bitmaps);
m_transport->send_commands(m_commands.buffer(), m_pending_bitmaps);
m_commands.clear_with_capacity();
m_pending_bitmaps.clear_with_capacity();
}
@ -40,13 +39,13 @@ ByteBuffer WebGLContextProxyBase::send_sync_call(ByteBuffer request)
if (m_lost)
return {};
flush_commands();
return m_transport->sync_call(m_canvas_id, move(request));
return m_transport->sync_call(move(request));
}
ReadPixelsResult WebGLContextProxyBase::read_pixels_robust_angle_into_shared_buffer(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei buf_size, Core::AnonymousBuffer const& pixels)
{
flush_commands();
return m_transport->read_pixels_robust_angle(m_canvas_id, x, y, width, height, format, type, buf_size, pixels);
return m_transport->read_pixels_robust_angle(x, y, width, height, format, type, buf_size, pixels);
}
void WebGLContextProxyBase::set_size(Gfx::IntSize const& size)
@ -57,7 +56,7 @@ void WebGLContextProxyBase::set_size(Gfx::IntSize const& size)
void WebGLContextProxyBase::present_canvas_for_compositing(bool preserve_drawing_buffer)
{
flush_commands();
m_transport->present_canvas(m_canvas_id, preserve_drawing_buffer);
m_transport->present_canvas(preserve_drawing_buffer);
}
RefPtr<Gfx::Bitmap> WebGLContextProxyBase::read_back_drawing_buffer(Gfx::IntRect const& rect)
@ -65,7 +64,7 @@ RefPtr<Gfx::Bitmap> WebGLContextProxyBase::read_back_drawing_buffer(Gfx::IntRect
if (m_lost)
return nullptr;
flush_commands();
auto bitmap = m_transport->read_back_drawing_buffer(m_canvas_id, rect);
auto bitmap = m_transport->read_back_drawing_buffer(rect);
if (!bitmap.is_valid())
return nullptr;
return bitmap.bitmap();
@ -138,7 +137,7 @@ void WebGLContextProxyBase::read_buffer_sub_data(GLenum target, long long offset
auto shared_data = shared_data_or_error.release_value_but_fixme_should_propagate_errors();
flush_commands();
m_transport->read_buffer_sub_data(m_canvas_id, target, static_cast<GLintptr>(offset), static_cast<GLintptr>(destination.size()), shared_data);
m_transport->read_buffer_sub_data(target, static_cast<GLintptr>(offset), static_cast<GLintptr>(destination.size()), shared_data);
if (m_lost)
return;
__builtin_memcpy(destination.data(), shared_data.data<void>(), destination.size());

View file

@ -31,11 +31,11 @@ class WEB_API WebGLContextProxyBase {
AK_MAKE_NONMOVABLE(WebGLContextProxyBase);
public:
WebGLContextProxyBase(NonnullRefPtr<RemoteWebGLTransport>, Painting::CanvasId, WebGLVersion, Vector<String> supported_extensions);
WebGLContextProxyBase(NonnullRefPtr<RemoteWebGLTransport>, WebGLVersion, Vector<String> supported_extensions);
~WebGLContextProxyBase();
void flush_commands();
Painting::CanvasId canvas_id() const { return m_canvas_id; }
Optional<Painting::CanvasId> canvas_id() const { return m_transport->canvas_id(); }
void make_current() { }
void notify_content_will_change() { }
@ -91,7 +91,6 @@ protected:
private:
NonnullRefPtr<RemoteWebGLTransport> m_transport;
Painting::CanvasId m_canvas_id { 0 };
WebGLVersion m_webgl_version { WebGLVersion::WebGL1 };
Vector<String> m_supported_extensions;
WebGLCommandList m_commands;

View file

@ -36,9 +36,40 @@ void WebGLObject::visit_edges(Visitor& visitor)
}
ErrorOr<GLuint> WebGLObject::handle(WebGLRenderingContextBase const* context) const
{
TRY(validate_context(context));
if (invalidated_for_context(context))
return Error::from_errno(GL_INVALID_OPERATION);
return m_handle;
}
ErrorOr<Optional<GLuint>> WebGLObject::handle_for_deletion(WebGLRenderingContextBase const* context)
{
TRY(validate_context(context));
if (invalidated_for_context(context))
return Optional<GLuint> {};
m_invalidated = true;
return Optional<GLuint> { m_handle };
}
ErrorOr<Optional<GLuint>> WebGLObject::handle_for_query(WebGLRenderingContextBase const* context) const
{
TRY(validate_context(context));
if (invalidated_for_context(context))
return Optional<GLuint> {};
return Optional<GLuint> { m_handle };
}
bool WebGLObject::invalidated_for_context(WebGLRenderingContextBase const* context) const
{
(void)context;
return m_invalidated;
}
ErrorOr<void> WebGLObject::validate_context(WebGLRenderingContextBase const* context) const
{
if (context == m_context)
return m_handle;
return {};
return Error::from_errno(GL_INVALID_OPERATION);
}

View file

@ -8,6 +8,7 @@
#pragma once
#include <AK/Optional.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/Export.h>
#include <LibWeb/WebGL/Types.h>
@ -25,6 +26,8 @@ public:
void set_label(String const& label) { m_label = label; }
ErrorOr<GLuint> handle(WebGLRenderingContextBase const* context) const;
ErrorOr<Optional<GLuint>> handle_for_deletion(WebGLRenderingContextBase const* context);
ErrorOr<Optional<GLuint>> handle_for_query(WebGLRenderingContextBase const* context) const;
protected:
explicit WebGLObject(JS::Realm&, GC::Ref<WebGLRenderingContextBase>, GLuint handle);
@ -33,6 +36,9 @@ protected:
void visit_edges(Visitor&) override;
bool invalidated() const { return m_invalidated; }
bool invalidated_for_context(WebGLRenderingContextBase const*) const;
void invalidate() { m_invalidated = true; }
ErrorOr<void> validate_context(WebGLRenderingContextBase const* context) const;
GC::Ref<WebGLRenderingContextBase> m_context;

View file

@ -5,18 +5,22 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/SkiaBackendContext.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/WebGLContextEvent.h>
#include <LibWeb/Bindings/WebGLRenderingContext.h>
#include <LibWeb/Compositor/CompositorHost.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/HTMLCanvasElement.h>
#include <LibWeb/HTML/Navigable.h>
#include <LibWeb/Infra/Strings.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/Painting/Paintable.h>
#include <LibWeb/WebGL/EventNames.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/RemoteWebGLTransport.h>
#include <LibWeb/WebGL/WebGLContextEvent.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContext.h>
#include <LibWeb/WebGL/WebGLShader.h>
#include <LibWeb/WebIDL/Buffers.h>
@ -28,15 +32,14 @@ namespace Web::WebGL {
GC_DEFINE_ALLOCATOR(WebGLRenderingContext);
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#fire-a-webgl-context-event
void fire_webgl_context_event(HTML::HTMLCanvasElement& canvas_element, FlyString const& type)
bool fire_webgl_context_event(HTML::HTMLCanvasElement& canvas_element, FlyString const& type)
{
// To fire a WebGL context event named e means that an event using the WebGLContextEvent interface, with its type attribute [DOM4] initialized to e, its cancelable attribute initialized to true, and its isTrusted attribute [DOM4] initialized to true, is to be dispatched at the given object.
// FIXME: Consider setting a status message.
auto event = WebGLContextEvent::create(canvas_element.realm(), type, Bindings::WebGLContextEventInit {});
event->set_is_trusted(true);
event->set_cancelable(true);
canvas_element.dispatch_event(*event);
return canvas_element.dispatch_event(*event);
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#fire-a-webgl-context-creation-error
@ -46,33 +49,53 @@ void fire_webgl_context_creation_error(HTML::HTMLCanvasElement& canvas_element)
fire_webgl_context_event(canvas_element, EventNames::webglcontextcreationerror);
}
// The drawing buffer's creation-time size, clamped like set_size() clamps resizes;
// later resizes travel as SetDrawingBufferSize commands.
static Gfx::IntSize initial_drawing_buffer_size(HTML::HTMLCanvasElement& canvas_element)
{
auto size = canvas_element.bitmap_size_for_canvas(1, 1);
return {
clamp(size.width(), 1, max_webgl_drawing_buffer_dimension),
clamp(size.height(), 1, max_webgl_drawing_buffer_dimension),
};
}
OwnPtr<WebGLContextProxy> create_webgl_context_proxy(HTML::HTMLCanvasElement& canvas_element, WebGLVersion webgl_version, WebGLContextAttributes const& context_attributes)
{
auto& page = canvas_element.document().page();
if (!page.has_compositor_host())
return {};
auto transport = page.compositor_host().create_webgl_transport();
if (!transport)
return {};
auto result = transport->create_context(
webgl_version,
initial_drawing_buffer_size(canvas_element),
context_attributes.depth,
context_attributes.stencil,
context_attributes.antialias);
if (!result.success)
return {};
return make<WebGLContextProxy>(transport.release_nonnull(), webgl_version, move(result.supported_extensions));
}
JS::ThrowCompletionOr<GC::Ptr<WebGLRenderingContext>> WebGLRenderingContext::create(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, JS::Value options)
{
// We should be coming here from getContext being called on a wrapped <canvas> element.
auto context_attributes = TRY(convert_value_to_context_attributes_dictionary(canvas_element.vm(), options));
auto skia_backend_context = Gfx::SkiaBackendContext::the_main_thread_context();
if (!skia_backend_context) {
fire_webgl_context_creation_error(canvas_element);
return GC::Ptr<WebGLRenderingContext> { nullptr };
}
OpenGLContext::DrawingBufferOptions context_options {
.depth = context_attributes.depth,
.stencil = context_attributes.stencil,
.antialias = context_attributes.antialias,
};
auto context = OpenGLContext::create(*skia_backend_context, OpenGLContext::WebGLVersion::WebGL1, context_options);
auto context = create_webgl_context_proxy(canvas_element, WebGLVersion::WebGL1, context_attributes);
if (!context) {
fire_webgl_context_creation_error(canvas_element);
return GC::Ptr<WebGLRenderingContext> { nullptr };
}
context->set_size(canvas_element.bitmap_size_for_canvas(1, 1));
return realm.create<WebGLRenderingContext>(realm, canvas_element, context.release_nonnull(), context_attributes, context_attributes);
}
WebGLRenderingContext::WebGLRenderingContext(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr<OpenGLContext> context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters)
WebGLRenderingContext::WebGLRenderingContext(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr<WebGLContextProxy> context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters)
: WebGLRenderingContextOverloads(realm, move(context))
, m_canvas_element(canvas_element)
, m_context_creation_parameters(context_creation_parameters)
@ -95,9 +118,9 @@ void WebGLRenderingContext::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_canvas_element);
}
void WebGLRenderingContext::present()
void WebGLRenderingContext::prepare_for_compositing()
{
context().present(m_context_creation_parameters.preserve_drawing_buffer);
context().present_canvas_for_compositing(m_context_creation_parameters.preserve_drawing_buffer);
}
GC::Ref<HTML::HTMLCanvasElement> WebGLRenderingContext::canvas_for_binding() const
@ -105,7 +128,7 @@ GC::Ref<HTML::HTMLCanvasElement> WebGLRenderingContext::canvas_for_binding() con
return *m_canvas_element;
}
void WebGLRenderingContext::needs_to_present()
void WebGLRenderingContext::did_update_canvas_content()
{
m_canvas_element->set_canvas_content_dirty();
@ -122,8 +145,8 @@ Optional<WebGLContextAttributes> WebGLRenderingContext::get_context_attributes()
void WebGLRenderingContext::set_size(Gfx::IntSize const& size)
{
Gfx::IntSize final_size;
final_size.set_width(max(size.width(), 1));
final_size.set_height(max(size.height(), 1));
final_size.set_width(clamp(size.width(), 1, max_webgl_drawing_buffer_dimension));
final_size.set_height(clamp(size.height(), 1, max_webgl_drawing_buffer_dimension));
context().set_size(final_size);
}
@ -131,26 +154,16 @@ void WebGLRenderingContext::reset_to_default_state()
{
}
RefPtr<Gfx::PaintingSurface> WebGLRenderingContext::surface()
{
return context().surface();
}
void WebGLRenderingContext::allocate_painting_surface_if_needed()
{
context().allocate_painting_surface_if_needed();
}
WebIDL::Long WebGLRenderingContext::drawing_buffer_width() const
{
auto size = canvas_for_binding()->bitmap_size_for_canvas();
return size.width();
return min(size.width(), max_webgl_drawing_buffer_dimension);
}
WebIDL::Long WebGLRenderingContext::drawing_buffer_height() const
{
auto size = canvas_for_binding()->bitmap_size_for_canvas();
return size.height();
return min(size.height(), max_webgl_drawing_buffer_dimension);
}
}

View file

@ -25,16 +25,13 @@ public:
virtual ~WebGLRenderingContext() override;
void present() override;
void needs_to_present() override;
void prepare_for_compositing() override;
void did_update_canvas_content() override;
GC::Ref<HTML::HTMLCanvasElement> canvas_for_binding() const;
virtual GC::Ref<HTML::HTMLCanvasElement> canvas_for_binding() const override;
Optional<WebGLContextAttributes> get_context_attributes();
RefPtr<Gfx::PaintingSurface> surface();
void allocate_painting_surface_if_needed();
void set_size(Gfx::IntSize const&);
void reset_to_default_state();
@ -44,7 +41,7 @@ public:
private:
virtual void initialize(JS::Realm&) override;
WebGLRenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr<OpenGLContext> context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters);
WebGLRenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr<WebGLContextProxy> context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters);
virtual void visit_edges(Cell::Visitor&) override;
@ -59,7 +56,9 @@ private:
WebGLContextAttributes m_actual_context_parameters {};
};
void fire_webgl_context_event(HTML::HTMLCanvasElement& canvas_element, FlyString const& type);
bool fire_webgl_context_event(HTML::HTMLCanvasElement& canvas_element, FlyString const& type);
void fire_webgl_context_creation_error(HTML::HTMLCanvasElement& canvas_element);
OwnPtr<WebGLContextProxy> create_webgl_context_proxy(HTML::HTMLCanvasElement&, WebGLVersion, WebGLContextAttributes const&);
}

View file

@ -11,9 +11,7 @@ extern "C" {
#include <GLES2/gl2ext_angle.h>
}
#include <LibGfx/BitmapExport.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibGfx/SkiaUtils.h>
#include <LibWeb/HTML/DecodedImageData.h>
#include <LibWeb/HTML/EventLoop/Task.h>
#include <LibWeb/HTML/HTMLCanvasElement.h>
@ -24,6 +22,7 @@ extern "C" {
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/UniversalGlobalScope.h>
#include <LibWeb/Platform/EventLoopPlugin.h>
#include <LibWeb/WebGL/EventNames.h>
#include <LibWeb/WebGL/Extensions/ANGLEInstancedArrays.h>
#include <LibWeb/WebGL/Extensions/EXTBlendMinMax.h>
#include <LibWeb/WebGL/Extensions/EXTColorBufferFloat.h>
@ -38,68 +37,12 @@ extern "C" {
#include <LibWeb/WebGL/Extensions/WebGLDebugRendererInfo.h>
#include <LibWeb/WebGL/Extensions/WebGLDrawBuffers.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContext.h>
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
#include <core/SkCanvas.h>
#include <core/SkColorSpace.h>
#include <core/SkColorType.h>
#include <core/SkImage.h>
#include <core/SkPixmap.h>
#include <core/SkSurface.h>
namespace Web::WebGL {
static constexpr Optional<Gfx::ExportFormat> determine_export_format(WebIDL::UnsignedLong format, WebIDL::UnsignedLong type)
{
switch (format) {
case GL_RGB:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::RGB888;
case GL_UNSIGNED_SHORT_5_6_5:
return Gfx::ExportFormat::RGB565;
default:
break;
}
break;
case GL_RGBA:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::RGBA8888;
case GL_UNSIGNED_SHORT_4_4_4_4:
// FIXME: This is not exactly the same as RGBA.
return Gfx::ExportFormat::RGBA4444;
case GL_UNSIGNED_SHORT_5_5_5_1:
return Gfx::ExportFormat::RGBA5551;
break;
default:
break;
}
break;
case GL_ALPHA:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::Alpha8;
default:
break;
}
break;
case GL_LUMINANCE:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::Gray8;
default:
break;
}
break;
default:
break;
}
dbgln("WebGL: Unsupported format and type combination. format: 0x{:04x}, type: 0x{:04x}", format, type);
return {};
}
WebGLRenderingContextBase::WebGLRenderingContextBase(JS::Realm& realm)
: Bindings::PlatformObject(realm)
{
@ -108,58 +51,58 @@ WebGLRenderingContextBase::WebGLRenderingContextBase(JS::Realm& realm)
struct Extension {
Vector<StringView> required_angle_extensions;
JS::ThrowCompletionOr<GC::Ref<JS::Object>> (*factory)(JS::Realm&, GC::Ref<WebGLRenderingContextBase>);
Optional<OpenGLContext::WebGLVersion> only_for_webgl_version { OptionalNone {} };
Optional<WebGLVersion> only_for_webgl_version { OptionalNone {} };
};
static HashMap<String, Extension, AK::ASCIICaseInsensitiveStringTraits> const& available_webgl_extensions()
{
static auto const& extensions = *new HashMap<String, Extension, AK::ASCIICaseInsensitiveStringTraits> {
// Khronos ratified WebGL Extensions
{ "ANGLE_instanced_arrays"_string, { { "GL_ANGLE_instanced_arrays"sv }, ANGLEInstancedArrays::create, OpenGLContext::WebGLVersion::WebGL1 } },
{ "EXT_blend_minmax"_string, { { "GL_EXT_blend_minmax"sv }, EXTBlendMinMax::create, OpenGLContext::WebGLVersion::WebGL1 } },
{ "EXT_frag_depth"_string, { { "GL_EXT_frag_depth"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL1 } },
{ "EXT_shader_texture_lod"_string, { { "GL_EXT_shader_texture_lod"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL1 } },
{ "ANGLE_instanced_arrays"_string, { { "GL_ANGLE_instanced_arrays"sv }, ANGLEInstancedArrays::create, WebGLVersion::WebGL1 } },
{ "EXT_blend_minmax"_string, { { "GL_EXT_blend_minmax"sv }, EXTBlendMinMax::create, WebGLVersion::WebGL1 } },
{ "EXT_frag_depth"_string, { { "GL_EXT_frag_depth"sv }, nullptr, WebGLVersion::WebGL1 } },
{ "EXT_shader_texture_lod"_string, { { "GL_EXT_shader_texture_lod"sv }, nullptr, WebGLVersion::WebGL1 } },
{ "EXT_texture_filter_anisotropic"_string, { { "GL_EXT_texture_filter_anisotropic"sv }, EXTTextureFilterAnisotropic::create } },
{ "OES_element_index_uint"_string, { { "GL_OES_element_index_uint"sv }, OESElementIndexUint::create, OpenGLContext::WebGLVersion::WebGL1 } },
{ "OES_standard_derivatives"_string, { { "GL_OES_standard_derivatives"sv }, OESStandardDerivatives::create, OpenGLContext::WebGLVersion::WebGL1 } },
{ "OES_texture_float"_string, { { "GL_OES_texture_float"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL1 } },
{ "OES_element_index_uint"_string, { { "GL_OES_element_index_uint"sv }, OESElementIndexUint::create, WebGLVersion::WebGL1 } },
{ "OES_standard_derivatives"_string, { { "GL_OES_standard_derivatives"sv }, OESStandardDerivatives::create, WebGLVersion::WebGL1 } },
{ "OES_texture_float"_string, { { "GL_OES_texture_float"sv }, nullptr, WebGLVersion::WebGL1 } },
{ "OES_texture_float_linear"_string, { { "GL_OES_texture_float_linear"sv }, nullptr } },
{ "OES_texture_half_float"_string, { { "GL_OES_texture_half_float"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL1 } },
{ "OES_texture_half_float_linear"_string, { { "GL_OES_texture_half_float_linear"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL1 } },
{ "OES_vertex_array_object"_string, { { "GL_OES_vertex_array_object"sv }, OESVertexArrayObject::create, OpenGLContext::WebGLVersion::WebGL1 } },
{ "OES_texture_half_float"_string, { { "GL_OES_texture_half_float"sv }, nullptr, WebGLVersion::WebGL1 } },
{ "OES_texture_half_float_linear"_string, { { "GL_OES_texture_half_float_linear"sv }, nullptr, WebGLVersion::WebGL1 } },
{ "OES_vertex_array_object"_string, { { "GL_OES_vertex_array_object"sv }, OESVertexArrayObject::create, WebGLVersion::WebGL1 } },
{ "WEBGL_compressed_texture_s3tc"_string, { { "GL_EXT_texture_compression_dxt1"sv, "GL_ANGLE_texture_compression_dxt3"sv, "GL_ANGLE_texture_compression_dxt5"sv }, WebGLCompressedTextureS3tc::create } },
{ "WEBGL_debug_renderer_info"_string, { {}, WebGLDebugRendererInfo::create } },
{ "WEBGL_debug_shaders"_string, { {}, nullptr } },
{ "WEBGL_depth_texture"_string, { { "GL_ANGLE_depth_texture"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL1 } },
{ "WEBGL_draw_buffers"_string, { { "GL_EXT_draw_buffers"sv }, WebGLDrawBuffers::create, OpenGLContext::WebGLVersion::WebGL1 } },
{ "WEBGL_depth_texture"_string, { { "GL_ANGLE_depth_texture"sv }, nullptr, WebGLVersion::WebGL1 } },
{ "WEBGL_draw_buffers"_string, { { "GL_EXT_draw_buffers"sv }, WebGLDrawBuffers::create, WebGLVersion::WebGL1 } },
{ "WEBGL_lose_context"_string, { {}, nullptr } },
// Community approved WebGL Extensions
{ "EXT_clip_control"_string, { { "GL_EXT_clip_control"sv }, nullptr } },
{ "EXT_color_buffer_float"_string, { { "GL_EXT_color_buffer_float"sv }, EXTColorBufferFloat::create, OpenGLContext::WebGLVersion::WebGL2 } },
{ "EXT_color_buffer_float"_string, { { "GL_EXT_color_buffer_float"sv }, EXTColorBufferFloat::create, WebGLVersion::WebGL2 } },
{ "EXT_color_buffer_half_float"_string, { { "GL_EXT_color_buffer_half_float"sv }, nullptr } },
{ "EXT_conservative_depth"_string, { { "GL_EXT_conservative_depth"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL2 } },
{ "EXT_conservative_depth"_string, { { "GL_EXT_conservative_depth"sv }, nullptr, WebGLVersion::WebGL2 } },
{ "EXT_depth_clamp"_string, { { "GL_EXT_depth_clamp"sv }, nullptr } },
{ "EXT_disjoint_timer_query"_string, { { "GL_EXT_disjoint_timer_query"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL1 } },
{ "EXT_disjoint_timer_query_webgl2"_string, { { "GL_EXT_disjoint_timer_query"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL2 } },
{ "EXT_disjoint_timer_query"_string, { { "GL_EXT_disjoint_timer_query"sv }, nullptr, WebGLVersion::WebGL1 } },
{ "EXT_disjoint_timer_query_webgl2"_string, { { "GL_EXT_disjoint_timer_query"sv }, nullptr, WebGLVersion::WebGL2 } },
{ "EXT_float_blend"_string, { { "GL_EXT_float_blend"sv }, nullptr } },
{ "EXT_polygon_offset_clamp"_string, { { "GL_EXT_polygon_offset_clamp"sv }, nullptr } },
{ "EXT_render_snorm"_string, { { "GL_EXT_render_snorm"sv }, EXTRenderSnorm::create, OpenGLContext::WebGLVersion::WebGL2 } },
{ "EXT_sRGB"_string, { { "GL_EXT_sRGB"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL1 } },
{ "EXT_render_snorm"_string, { { "GL_EXT_render_snorm"sv }, EXTRenderSnorm::create, WebGLVersion::WebGL2 } },
{ "EXT_sRGB"_string, { { "GL_EXT_sRGB"sv }, nullptr, WebGLVersion::WebGL1 } },
{ "EXT_texture_compression_bptc"_string, { { "GL_EXT_texture_compression_bptc"sv }, nullptr } },
{ "EXT_texture_compression_rgtc"_string, { { "GL_EXT_texture_compression_rgtc"sv }, nullptr } },
{ "EXT_texture_mirror_clamp_to_edge"_string, { { "GL_EXT_texture_mirror_clamp_to_edge"sv }, nullptr } },
{ "EXT_texture_norm16"_string, { { "GL_EXT_texture_norm16"sv }, EXTTextureNorm16::create, OpenGLContext::WebGLVersion::WebGL2 } },
{ "EXT_texture_norm16"_string, { { "GL_EXT_texture_norm16"sv }, EXTTextureNorm16::create, WebGLVersion::WebGL2 } },
{ "KHR_parallel_shader_compile"_string, { { "GL_KHR_parallel_shader_compile"sv }, nullptr } },
{ "NV_shader_noperspective_interpolation"_string, { { "GL_NV_shader_noperspective_interpolation"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL2 } },
{ "NV_shader_noperspective_interpolation"_string, { { "GL_NV_shader_noperspective_interpolation"sv }, nullptr, WebGLVersion::WebGL2 } },
{ "OES_draw_buffers_indexed"_string, { { "GL_OES_draw_buffers_indexed"sv }, nullptr } },
{ "OES_fbo_render_mipmap"_string, { { "GL_OES_fbo_render_mipmap"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL1 } },
{ "OES_sample_variables"_string, { { "GL_OES_sample_variables"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL2 } },
{ "OES_shader_multisample_interpolation"_string, { { "GL_OES_shader_multisample_interpolation"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL2 } },
{ "OVR_multiview2"_string, { { "GL_OVR_multiview2"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL2 } },
{ "OES_fbo_render_mipmap"_string, { { "GL_OES_fbo_render_mipmap"sv }, nullptr, WebGLVersion::WebGL1 } },
{ "OES_sample_variables"_string, { { "GL_OES_sample_variables"sv }, nullptr, WebGLVersion::WebGL2 } },
{ "OES_shader_multisample_interpolation"_string, { { "GL_OES_shader_multisample_interpolation"sv }, nullptr, WebGLVersion::WebGL2 } },
{ "OVR_multiview2"_string, { { "GL_OVR_multiview2"sv }, nullptr, WebGLVersion::WebGL2 } },
{ "WEBGL_blend_func_extended"_string, { { "GL_EXT_blend_func_extended"sv }, nullptr } },
{ "WEBGL_clip_cull_distance"_string, { { "GL_EXT_clip_cull_distance"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL2 } },
{ "WEBGL_color_buffer_float"_string, { { "EXT_color_buffer_half_float"sv, "OES_texture_float"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL1 } },
{ "WEBGL_clip_cull_distance"_string, { { "GL_EXT_clip_cull_distance"sv }, nullptr, WebGLVersion::WebGL2 } },
{ "WEBGL_color_buffer_float"_string, { { "EXT_color_buffer_half_float"sv, "OES_texture_float"sv }, nullptr, WebGLVersion::WebGL1 } },
{ "WEBGL_compressed_texture_astc"_string, { { "KHR_texture_compression_astc_hdr"sv, "KHR_texture_compression_astc_ldr"sv }, nullptr } },
{ "WEBGL_compressed_texture_etc"_string, { { "GL_ANGLE_compressed_texture_etc"sv }, nullptr } },
{ "WEBGL_compressed_texture_etc1"_string, { { "GL_OES_compressed_ETC1_RGB8_texture"sv }, nullptr } },
@ -167,16 +110,16 @@ static HashMap<String, Extension, AK::ASCIICaseInsensitiveStringTraits> const& a
{ "WEBGL_compressed_texture_s3tc_srgb"_string, { { "GL_EXT_texture_compression_s3tc_srgb"sv }, WebGLCompressedTextureS3tcSrgb::create } },
{ "WEBGL_multi_draw"_string, { { "GL_ANGLE_multi_draw"sv }, nullptr } },
{ "WEBGL_polygon_mode"_string, { { "GL_ANGLE_polygon_mode"sv }, nullptr } },
{ "WEBGL_provoking_vertex"_string, { { "GL_ANGLE_provoking_vertex"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL2 } },
{ "WEBGL_render_shared_exponent"_string, { { "GL_QCOM_render_shared_exponent"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL2 } },
{ "WEBGL_stencil_texturing"_string, { { "GL_ANGLE_stencil_texturing"sv }, nullptr, OpenGLContext::WebGLVersion::WebGL2 } },
{ "WEBGL_provoking_vertex"_string, { { "GL_ANGLE_provoking_vertex"sv }, nullptr, WebGLVersion::WebGL2 } },
{ "WEBGL_render_shared_exponent"_string, { { "GL_QCOM_render_shared_exponent"sv }, nullptr, WebGLVersion::WebGL2 } },
{ "WEBGL_stencil_texturing"_string, { { "GL_ANGLE_stencil_texturing"sv }, nullptr, WebGLVersion::WebGL2 } },
};
return extensions;
}
Optional<Vector<String>> WebGLRenderingContextBase::get_supported_extensions()
{
auto opengl_extensions = context().get_supported_opengl_extensions();
auto const& opengl_extensions = context().get_supported_opengl_extensions();
Vector<String> webgl_extensions;
for (auto const& [available_extension_name, available_extension_info] : available_webgl_extensions()) {
@ -228,7 +171,7 @@ JS::Object* WebGLRenderingContextBase::get_extension(String const& name)
return nullptr;
for (auto const& required_extension : extension_info.required_angle_extensions) {
context().request_extension(null_terminated_string(required_extension).data());
context().request_extension_angle(null_terminated_string(required_extension).data());
}
auto extension = MUST(extension_info.factory(realm(), *this));
@ -257,7 +200,7 @@ ReadonlySpan<WebIDL::UnsignedLong> WebGLRenderingContextBase::enabled_compressed
return m_enabled_compressed_texture_formats;
}
Optional<Gfx::BitmapExportResult> WebGLRenderingContextBase::read_and_pixel_convert_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Optional<int> destination_width, Optional<int> destination_height)
Optional<WebGLRenderingContextBase::TexImageSourceFrame> WebGLRenderingContextBase::read_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type)
{
// FIXME: If this function is called with an ImageData whose data attribute has been neutered,
// an INVALID_VALUE error is generated.
@ -273,10 +216,7 @@ Optional<Gfx::BitmapExportResult> WebGLRenderingContextBase::read_and_pixel_conv
return source->current_image_frame();
},
[](GC::Ref<HTML::HTMLCanvasElement> source) -> Optional<Gfx::DecodedImageFrame> {
auto surface = source->surface();
if (!surface)
return Gfx::DecodedImageFrame { *source->get_bitmap_from_surface() };
return Gfx::DecodedImageFrame { *surface->snapshot_bitmap() };
return Gfx::DecodedImageFrame { *source->get_bitmap_from_surface() };
},
[](GC::Ref<HTML::OffscreenCanvas> source) -> Optional<Gfx::DecodedImageFrame> {
return Gfx::DecodedImageFrame { *source->bitmap() };
@ -293,41 +233,34 @@ Optional<Gfx::BitmapExportResult> WebGLRenderingContextBase::read_and_pixel_conv
if (!frame.has_value())
return OptionalNone {};
auto export_format = determine_export_format(format, type);
if (!export_format.has_value())
// Validate the combination before recording; the pixels travel to the host as shared
// memory and the host performs the conversion next to GL.
if (!texture_export_format(format, type).has_value())
return OptionalNone {};
// FIXME: Respect unpackColorSpace
auto export_flags = 0;
if (m_unpack_flip_y && !source.has<GC::Ref<HTML::ImageBitmap>>())
return TexImageSourceFrame {
.frame = frame.release_value(),
// The first pixel transferred from the source to the WebGL implementation corresponds to the upper left corner of
// the source. This behavior is modified by the UNPACK_FLIP_Y_WEBGL pixel storage parameter, except for ImageBitmap
// arguments, as described in the abovementioned section.
export_flags |= Gfx::ExportFlags::FlipY;
if (m_unpack_premultiply_alpha)
export_flags |= Gfx::ExportFlags::PremultiplyAlpha;
auto result = Gfx::export_bitmap_to_byte_buffer(
frame->bitmap(),
frame->color_space(),
export_format.value(),
export_flags,
destination_width,
destination_height);
if (result.is_error()) {
dbgln("Could not export bitmap: {}", result.release_error());
return OptionalNone {};
}
return result.release_value();
.flip_y = m_unpack_flip_y && !source.has<GC::Ref<HTML::ImageBitmap>>(),
.premultiply_alpha = m_unpack_premultiply_alpha,
};
}
// TODO: The glGetError spec allows for queueing errors which is something we should probably do, for now
// this just keeps track of one error which is also fine by the spec
GLenum WebGLRenderingContextBase::get_error_value()
{
if (m_error == GL_NO_ERROR)
return context().get_error();
// A locally-detected failure (currently an upload too large to send over IPC) is reported
// before consulting the host.
if (auto local_error = context().take_pending_local_error(); local_error != GL_NO_ERROR)
return local_error;
auto context_error = context().get_error();
if (context_error != GL_NO_ERROR)
return context_error;
auto error = m_error;
m_error = GL_NO_ERROR;
@ -336,13 +269,7 @@ GLenum WebGLRenderingContextBase::get_error_value()
void WebGLRenderingContextBase::set_error(GLenum error)
{
if (m_error != GL_NO_ERROR)
return;
auto context_error = context().get_error();
if (context_error != GL_NO_ERROR)
m_error = context_error;
else
if (m_error == GL_NO_ERROR)
m_error = error;
}

View file

@ -6,7 +6,7 @@
#pragma once
#include <LibGfx/BitmapExport.h>
#include <LibGfx/DecodedImageFrame.h>
#include <LibJS/Runtime/DataView.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibWeb/Bindings/PlatformObject.h>
@ -45,7 +45,8 @@ public:
using Int32List = Variant<GC::Ref<JS::Int32Array>, Vector<WebIDL::Long>>;
using Uint32List = Variant<GC::Ref<JS::Uint32Array>, Vector<WebIDL::UnsignedLong>>;
virtual OpenGLContext& context() = 0;
virtual WebGLContextProxy& context() = 0;
virtual GC::Ref<HTML::HTMLCanvasElement> canvas_for_binding() const = 0;
bool is_context_lost() const;
@ -133,7 +134,12 @@ protected:
return get_offset_span(buffer->data(), src_offset, src_length_override);
}
Optional<Gfx::BitmapExportResult> read_and_pixel_convert_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Optional<int> destination_width = OptionalNone {}, Optional<int> destination_height = OptionalNone {});
struct TexImageSourceFrame {
Gfx::DecodedImageFrame frame;
bool flip_y { false };
bool premultiply_alpha { false };
};
Optional<TexImageSourceFrame> read_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type);
static Vector<GLchar> null_terminated_string(StringView string)
{

View file

@ -16,9 +16,9 @@ extern "C" {
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/DataView.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLActiveInfo.h>
#include <LibWeb/WebGL/WebGLBuffer.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLFramebuffer.h>
#include <LibWeb/WebGL/WebGLProgram.h>
#include <LibWeb/WebGL/WebGLQuery.h>
@ -40,7 +40,7 @@ namespace Web::WebGL {
static constexpr GLenum UNMASKED_VENDOR_WEBGL = 0x9245;
static constexpr GLenum UNMASKED_RENDERER_WEBGL = 0x9246;
WebGLRenderingContextImpl::WebGLRenderingContextImpl(JS::Realm& realm, NonnullOwnPtr<OpenGLContext> context)
WebGLRenderingContextImpl::WebGLRenderingContextImpl(JS::Realm& realm, NonnullOwnPtr<WebGLContextProxy> context)
: WebGLRenderingContextBase(realm)
, m_context(move(context))
{
@ -137,7 +137,7 @@ void WebGLRenderingContextImpl::bind_buffer(WebIDL::UnsignedLong target, GC::Ptr
}
}
if (m_context->webgl_version() == OpenGLContext::WebGLVersion::WebGL2) {
if (m_context->webgl_version() == WebGLVersion::WebGL2) {
switch (target) {
case GL_ARRAY_BUFFER:
m_array_buffer_binding = buffer;
@ -245,7 +245,7 @@ void WebGLRenderingContextImpl::bind_texture(WebIDL::UnsignedLong target, GC::Pt
break;
case GL_TEXTURE_2D_ARRAY:
if (m_context->webgl_version() == OpenGLContext::WebGLVersion::WebGL2) {
if (m_context->webgl_version() == WebGLVersion::WebGL2) {
m_texture_binding_2d_array = texture;
break;
}
@ -253,7 +253,7 @@ void WebGLRenderingContextImpl::bind_texture(WebIDL::UnsignedLong target, GC::Pt
set_error(GL_INVALID_ENUM);
return;
case GL_TEXTURE_3D:
if (m_context->webgl_version() == OpenGLContext::WebGLVersion::WebGL2) {
if (m_context->webgl_version() == WebGLVersion::WebGL2) {
m_texture_binding_3d = texture;
break;
}
@ -309,7 +309,7 @@ void WebGLRenderingContextImpl::clear(WebIDL::UnsignedLong mask)
{
m_context->make_current();
m_context->notify_content_will_change();
needs_to_present();
did_update_canvas_content();
m_context->clear(mask);
}
@ -428,51 +428,59 @@ void WebGLRenderingContextImpl::delete_buffer(GC::Ptr<WebGLBuffer> buffer)
{
m_context->make_current();
GLuint buffer_handle = 0;
if (buffer) {
auto handle_or_error = buffer->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
buffer_handle = handle_or_error.release_value();
}
if (!buffer)
return;
m_context->delete_buffers(1, &buffer_handle);
auto handle_or_error = buffer->handle_for_deletion(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
auto buffer_handle = handle_or_error.release_value();
if (!buffer_handle.has_value())
return;
auto handle = buffer_handle.value();
m_context->delete_buffers(1, &handle);
}
void WebGLRenderingContextImpl::delete_framebuffer(GC::Ptr<WebGLFramebuffer> framebuffer)
{
m_context->make_current();
GLuint framebuffer_handle = 0;
if (framebuffer) {
auto handle_or_error = framebuffer->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
framebuffer_handle = handle_or_error.release_value();
}
if (!framebuffer)
return;
m_context->delete_framebuffers(1, &framebuffer_handle);
auto handle_or_error = framebuffer->handle_for_deletion(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
auto framebuffer_handle = handle_or_error.release_value();
if (!framebuffer_handle.has_value())
return;
auto handle = framebuffer_handle.value();
m_context->delete_framebuffers(1, &handle);
}
void WebGLRenderingContextImpl::delete_program(GC::Ptr<WebGLProgram> program)
{
m_context->make_current();
auto program_handle = 0;
if (program) {
auto handle_or_error = program->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
program_handle = handle_or_error.release_value();
}
if (!program)
return;
m_context->delete_program(program_handle);
auto handle_or_error = program->handle_for_deletion(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
auto program_handle = handle_or_error.release_value();
if (!program_handle.has_value())
return;
m_context->delete_program(program_handle.value());
if (m_current_program == program)
m_current_program = nullptr;
}
@ -481,50 +489,59 @@ void WebGLRenderingContextImpl::delete_renderbuffer(GC::Ptr<WebGLRenderbuffer> r
{
m_context->make_current();
GLuint renderbuffer_handle = 0;
if (renderbuffer) {
auto handle_or_error = renderbuffer->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
renderbuffer_handle = handle_or_error.release_value();
}
if (!renderbuffer)
return;
m_context->delete_renderbuffers(1, &renderbuffer_handle);
auto handle_or_error = renderbuffer->handle_for_deletion(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
auto renderbuffer_handle = handle_or_error.release_value();
if (!renderbuffer_handle.has_value())
return;
auto handle = renderbuffer_handle.value();
m_context->delete_renderbuffers(1, &handle);
}
void WebGLRenderingContextImpl::delete_shader(GC::Ptr<WebGLShader> shader)
{
m_context->make_current();
auto shader_handle = 0;
if (shader) {
auto handle_or_error = shader->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
shader_handle = handle_or_error.release_value();
if (!shader)
return;
auto handle_or_error = shader->handle_for_deletion(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
m_context->delete_shader(shader_handle);
auto shader_handle = handle_or_error.release_value();
if (!shader_handle.has_value())
return;
m_context->delete_shader(shader_handle.value());
}
void WebGLRenderingContextImpl::delete_texture(GC::Ptr<WebGLTexture> texture)
{
m_context->make_current();
GLuint texture_handle = 0;
if (texture) {
auto handle_or_error = texture->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
texture_handle = handle_or_error.release_value();
}
if (!texture)
return;
m_context->delete_textures(1, &texture_handle);
auto handle_or_error = texture->handle_for_deletion(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return;
}
auto texture_handle = handle_or_error.release_value();
if (!texture_handle.has_value())
return;
auto handle = texture_handle.value();
m_context->delete_textures(1, &handle);
if (m_texture_binding_2d == texture)
m_texture_binding_2d = nullptr;
@ -600,7 +617,7 @@ void WebGLRenderingContextImpl::draw_arrays(WebIDL::UnsignedLong mode, WebIDL::L
{
m_context->make_current();
m_context->notify_content_will_change();
needs_to_present();
did_update_canvas_content();
m_context->draw_arrays(mode, first, count);
}
@ -610,7 +627,7 @@ void WebGLRenderingContextImpl::draw_elements(WebIDL::UnsignedLong mode, WebIDL:
m_context->notify_content_will_change();
m_context->draw_elements(mode, count, type, reinterpret_cast<void*>(offset));
needs_to_present();
did_update_canvas_content();
}
void WebGLRenderingContextImpl::enable(WebIDL::UnsignedLong cap)
@ -1268,7 +1285,7 @@ WebIDL::ExceptionOr<JS::Value> WebGLRenderingContextImpl::get_parameter(WebIDL::
}
case GL_FRAGMENT_SHADER_DERIVATIVE_HINT: { // NOTE: This has the same value as GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES
if (extension_enabled("OES_standard_derivatives"sv) || m_context->webgl_version() == OpenGLContext::WebGLVersion::WebGL2) {
if (extension_enabled("OES_standard_derivatives"sv) || m_context->webgl_version() == WebGLVersion::WebGL2) {
GLint result { 0 };
m_context->get_integerv_robust_angle(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, 1, nullptr, &result);
return JS::Value(result);
@ -1278,7 +1295,7 @@ WebIDL::ExceptionOr<JS::Value> WebGLRenderingContextImpl::get_parameter(WebIDL::
return JS::js_null();
}
case GL_MAX_COLOR_ATTACHMENTS: { // NOTE: This has the same value as MAX_COLOR_ATTACHMENTS_WEBGL
if (extension_enabled("WEBGL_draw_buffers"sv) || m_context->webgl_version() == OpenGLContext::WebGLVersion::WebGL2) {
if (extension_enabled("WEBGL_draw_buffers"sv) || m_context->webgl_version() == WebGLVersion::WebGL2) {
GLint result { 0 };
m_context->get_integerv_robust_angle(GL_MAX_COLOR_ATTACHMENTS, 1, nullptr, &result);
return JS::Value(result);
@ -1288,7 +1305,7 @@ WebIDL::ExceptionOr<JS::Value> WebGLRenderingContextImpl::get_parameter(WebIDL::
return JS::js_null();
}
case GL_MAX_DRAW_BUFFERS: {
if (m_context->webgl_version() == OpenGLContext::WebGLVersion::WebGL2) { // FIXME: Allow this code path for MAX_DRAW_BUFFERS_WEBGL
if (m_context->webgl_version() == WebGLVersion::WebGL2) { // FIXME: Allow this code path for MAX_DRAW_BUFFERS_WEBGL
GLint result { 0 };
m_context->get_integerv_robust_angle(GL_MAX_DRAW_BUFFERS, 1, nullptr, &result);
return JS::Value(result);
@ -1322,7 +1339,7 @@ WebIDL::ExceptionOr<JS::Value> WebGLRenderingContextImpl::get_parameter(WebIDL::
return JS::Value(m_unpack_colorspace_conversion);
}
if (m_context->webgl_version() == OpenGLContext::WebGLVersion::WebGL2) {
if (m_context->webgl_version() == WebGLVersion::WebGL2) {
switch (pname) {
case GL_COPY_READ_BUFFER_BINDING: {
if (!m_copy_read_buffer_binding)
@ -1560,8 +1577,9 @@ WebIDL::ExceptionOr<JS::Value> WebGLRenderingContextImpl::get_parameter(WebIDL::
return JS::Value(m_current_vertex_array);
}
case MAX_CLIENT_WAIT_TIMEOUT_WEBGL:
// FIXME: Make this an actual limit
return JS::js_infinity();
// A page must never be able to block the compositor, so clientWaitSync
// never waits; the host clamps the timeout to zero to match.
return JS::Value(0);
}
}
@ -1598,7 +1616,7 @@ JS::Value WebGLRenderingContextImpl::get_program_parameter(GC::Ref<WebGLProgram>
case GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
case GL_TRANSFORM_FEEDBACK_VARYINGS:
case GL_ACTIVE_UNIFORM_BLOCKS:
if (m_context->webgl_version() == OpenGLContext::WebGLVersion::WebGL2)
if (m_context->webgl_version() == WebGLVersion::WebGL2)
return JS::Value(result);
set_error(GL_INVALID_ENUM);
@ -1765,7 +1783,7 @@ JS::Value WebGLRenderingContextImpl::get_tex_parameter(WebIDL::UnsignedLong targ
}
}
if (m_context->webgl_version() == OpenGLContext::WebGLVersion::WebGL2) {
if (m_context->webgl_version() == WebGLVersion::WebGL2) {
switch (pname) {
case GL_TEXTURE_BASE_LEVEL:
case GL_TEXTURE_COMPARE_FUNC:
@ -1842,7 +1860,7 @@ JS::Value WebGLRenderingContextImpl::get_vertex_attrib(WebIDL::UnsignedLong inde
return WebGLBuffer::create(realm(), *this, handle);
}
case GL_VERTEX_ATTRIB_ARRAY_DIVISOR: { // NOTE: This has the same value as GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE
if (extension_enabled("ANGLE_instanced_arrays"sv) || m_context->webgl_version() == OpenGLContext::WebGLVersion::WebGL2) {
if (extension_enabled("ANGLE_instanced_arrays"sv) || m_context->webgl_version() == WebGLVersion::WebGL2) {
GLint result { 0 };
m_context->get_vertex_attribiv_robust_angle(index, GL_VERTEX_ATTRIB_ARRAY_DIVISOR, 1, nullptr, &result);
return JS::Value(result);
@ -1857,7 +1875,7 @@ JS::Value WebGLRenderingContextImpl::get_vertex_attrib(WebIDL::UnsignedLong inde
return JS::Value(result == GL_TRUE);
}
case GL_VERTEX_ATTRIB_ARRAY_INTEGER: {
if (m_context->webgl_version() == OpenGLContext::WebGLVersion::WebGL2) {
if (m_context->webgl_version() == WebGLVersion::WebGL2) {
GLint result { 0 };
m_context->get_vertex_attribiv_robust_angle(index, GL_VERTEX_ATTRIB_ARRAY_INTEGER, 1, nullptr, &result);
return JS::Value(result == GL_TRUE);
@ -1915,16 +1933,18 @@ bool WebGLRenderingContextImpl::is_buffer(GC::Ptr<WebGLBuffer> buffer)
{
m_context->make_current();
auto buffer_handle = 0;
if (buffer) {
auto handle_or_error = buffer->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
buffer_handle = handle_or_error.release_value();
if (!buffer)
return false;
auto handle_or_error = buffer->handle_for_query(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
return m_context->is_buffer(buffer_handle);
auto buffer_handle = handle_or_error.release_value();
if (!buffer_handle.has_value())
return false;
return m_context->is_buffer(buffer_handle.value());
}
bool WebGLRenderingContextImpl::is_enabled(WebIDL::UnsignedLong cap)
@ -1937,80 +1957,90 @@ bool WebGLRenderingContextImpl::is_framebuffer(GC::Ptr<WebGLFramebuffer> framebu
{
m_context->make_current();
auto framebuffer_handle = 0;
if (framebuffer) {
auto handle_or_error = framebuffer->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
framebuffer_handle = handle_or_error.release_value();
if (!framebuffer)
return false;
auto handle_or_error = framebuffer->handle_for_query(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
return m_context->is_framebuffer(framebuffer_handle);
auto framebuffer_handle = handle_or_error.release_value();
if (!framebuffer_handle.has_value())
return false;
return m_context->is_framebuffer(framebuffer_handle.value());
}
bool WebGLRenderingContextImpl::is_program(GC::Ptr<WebGLProgram> program)
{
m_context->make_current();
auto program_handle = 0;
if (program) {
auto handle_or_error = program->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
program_handle = handle_or_error.release_value();
if (!program)
return false;
auto handle_or_error = program->handle_for_query(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
return m_context->is_program(program_handle);
auto program_handle = handle_or_error.release_value();
if (!program_handle.has_value())
return false;
return m_context->is_program(program_handle.value());
}
bool WebGLRenderingContextImpl::is_renderbuffer(GC::Ptr<WebGLRenderbuffer> renderbuffer)
{
m_context->make_current();
auto renderbuffer_handle = 0;
if (renderbuffer) {
auto handle_or_error = renderbuffer->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
renderbuffer_handle = handle_or_error.release_value();
if (!renderbuffer)
return false;
auto handle_or_error = renderbuffer->handle_for_query(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
return m_context->is_renderbuffer(renderbuffer_handle);
auto renderbuffer_handle = handle_or_error.release_value();
if (!renderbuffer_handle.has_value())
return false;
return m_context->is_renderbuffer(renderbuffer_handle.value());
}
bool WebGLRenderingContextImpl::is_shader(GC::Ptr<WebGLShader> shader)
{
m_context->make_current();
auto shader_handle = 0;
if (shader) {
auto handle_or_error = shader->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
shader_handle = handle_or_error.release_value();
if (!shader)
return false;
auto handle_or_error = shader->handle_for_query(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
return m_context->is_shader(shader_handle);
auto shader_handle = handle_or_error.release_value();
if (!shader_handle.has_value())
return false;
return m_context->is_shader(shader_handle.value());
}
bool WebGLRenderingContextImpl::is_texture(GC::Ptr<WebGLTexture> texture)
{
m_context->make_current();
auto texture_handle = 0;
if (texture) {
auto handle_or_error = texture->handle(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
texture_handle = handle_or_error.release_value();
if (!texture)
return false;
auto handle_or_error = texture->handle_for_query(this);
if (handle_or_error.is_error()) {
set_error(GL_INVALID_OPERATION);
return false;
}
return m_context->is_texture(texture_handle);
auto texture_handle = handle_or_error.release_value();
if (!texture_handle.has_value())
return false;
return m_context->is_texture(texture_handle.value());
}
void WebGLRenderingContextImpl::line_width(float width)

View file

@ -24,12 +24,12 @@ class WebGLRenderingContextImpl : public WebGLRenderingContextBase {
WEB_NON_IDL_PLATFORM_OBJECT(WebGLRenderingContextImpl, WebGLRenderingContextBase);
public:
WebGLRenderingContextImpl(JS::Realm&, NonnullOwnPtr<OpenGLContext>);
WebGLRenderingContextImpl(JS::Realm&, NonnullOwnPtr<WebGLContextProxy>);
virtual OpenGLContext& context() override { return *m_context; }
virtual WebGLContextProxy& context() override { return *m_context; }
virtual void present() = 0;
virtual void needs_to_present() = 0;
virtual void prepare_for_compositing() = 0;
virtual void did_update_canvas_content() = 0;
void active_texture(WebIDL::UnsignedLong texture);
void attach_shader(GC::Ref<WebGLProgram> program, GC::Ref<WebGLShader> shader);
@ -171,7 +171,7 @@ protected:
GC::Ptr<WebGLQuery> m_any_samples_passed_conservative;
GC::Ptr<WebGLQuery> m_transform_feedback_primitives_written;
NonnullOwnPtr<OpenGLContext> m_context;
NonnullOwnPtr<WebGLContextProxy> m_context;
};
}

View file

@ -15,13 +15,13 @@ extern "C" {
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/DataView.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibWeb/WebGL/OpenGLContext.h>
#include <LibWeb/WebGL/WebGLContextProxy.h>
#include <LibWeb/WebGL/WebGLRenderingContextOverloads.h>
#include <LibWeb/WebGL/WebGLUniformLocation.h>
namespace Web::WebGL {
WebGLRenderingContextOverloads::WebGLRenderingContextOverloads(JS::Realm& realm, NonnullOwnPtr<OpenGLContext> context)
WebGLRenderingContextOverloads::WebGLRenderingContextOverloads(JS::Realm& realm, NonnullOwnPtr<WebGLContextProxy> context)
: WebGLRenderingContextImpl(realm, move(context))
{
}
@ -167,11 +167,11 @@ void WebGLRenderingContextOverloads::tex_image2d(WebIDL::UnsignedLong target, We
{
m_context->make_current();
auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type);
if (!maybe_converted_texture.has_value())
auto maybe_source_frame = read_texture_image_source(source, format, type);
if (!maybe_source_frame.has_value())
return;
auto converted_texture = maybe_converted_texture.release_value();
m_context->tex_image2d_robust_angle(target, level, internalformat, converted_texture.width, converted_texture.height, 0, format, type, converted_texture.buffer.size(), converted_texture.buffer.data());
auto source_frame = maybe_source_frame.release_value();
m_context->tex_image2d_from_bitmap(target, level, internalformat, format, type, move(source_frame.frame), OptionalNone {}, source_frame.flip_y, source_frame.premultiply_alpha);
}
void WebGLRenderingContextOverloads::tex_sub_image2d(WebIDL::UnsignedLong target, WebIDL::Long level, WebIDL::Long xoffset, WebIDL::Long yoffset, WebIDL::Long width, WebIDL::Long height, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, WebIDL::NullableArrayBufferViewVariant pixels)
@ -191,12 +191,11 @@ void WebGLRenderingContextOverloads::tex_sub_image2d(WebIDL::UnsignedLong target
{
m_context->make_current();
auto maybe_converted_texture = read_and_pixel_convert_texture_image_source(source, format, type);
if (!maybe_converted_texture.has_value())
auto maybe_source_frame = read_texture_image_source(source, format, type);
if (!maybe_source_frame.has_value())
return;
auto converted_texture = maybe_converted_texture.release_value();
m_context->tex_sub_image2d_robust_angle(target, level, xoffset, yoffset, converted_texture.width, converted_texture.height, format, type, converted_texture.buffer.size(), converted_texture.buffer.data());
auto source_frame = maybe_source_frame.release_value();
m_context->tex_sub_image2d_from_bitmap(target, level, xoffset, yoffset, format, type, move(source_frame.frame), OptionalNone {}, source_frame.flip_y, source_frame.premultiply_alpha);
}
void WebGLRenderingContextOverloads::uniform1fv(GC::Ptr<WebGLUniformLocation> location, Float32List v)

View file

@ -23,7 +23,7 @@ class WebGLRenderingContextOverloads : public WebGLRenderingContextImpl {
WEB_NON_IDL_PLATFORM_OBJECT(WebGLRenderingContextOverloads, WebGLRenderingContextImpl);
public:
WebGLRenderingContextOverloads(JS::Realm&, NonnullOwnPtr<OpenGLContext>);
WebGLRenderingContextOverloads(JS::Realm&, NonnullOwnPtr<WebGLContextProxy>);
void buffer_data(WebIDL::UnsignedLong target, WebIDL::LongLong size, WebIDL::UnsignedLong usage);
void buffer_data(WebIDL::UnsignedLong target, WebIDL::NullableBufferSourceVariant data, WebIDL::UnsignedLong usage);

View file

@ -36,9 +36,19 @@ void WebGLSync::initialize(JS::Realm& realm)
ErrorOr<GLsyncInternal> WebGLSync::sync_handle(WebGLRenderingContextBase const* context) const
{
if (context == m_context)
return m_sync_handle;
return Error::from_errno(GL_INVALID_OPERATION);
TRY(validate_context(context));
if (invalidated_for_context(context))
return Error::from_errno(GL_INVALID_OPERATION);
return m_sync_handle;
}
ErrorOr<Optional<GLsyncInternal>> WebGLSync::sync_handle_for_deletion(WebGLRenderingContextBase const* context)
{
TRY(validate_context(context));
if (invalidated_for_context(context))
return Optional<GLsyncInternal> {};
invalidate();
return Optional<GLsyncInternal> { m_sync_handle };
}
}

View file

@ -21,6 +21,7 @@ public:
virtual ~WebGLSync() override;
ErrorOr<GLsyncInternal> sync_handle(WebGLRenderingContextBase const* context) const;
ErrorOr<Optional<GLsyncInternal>> sync_handle_for_deletion(WebGLRenderingContextBase const* context);
protected:
explicit WebGLSync(JS::Realm&, GC::Ref<WebGLRenderingContextBase>, GLsyncInternal handle);

View file

@ -21,78 +21,17 @@ using namespace Web::WebGL;
static constexpr GLsizei max_webgl_string_list_entries = 16384;
static Web::WebGL::OpenGLContext::WebGLVersion to_opengl_webgl_version(WebGLVersion version)
{
switch (version) {
case WebGLVersion::WebGL1:
return Web::WebGL::OpenGLContext::WebGLVersion::WebGL1;
case WebGLVersion::WebGL2:
return Web::WebGL::OpenGLContext::WebGLVersion::WebGL2;
}
VERIFY_NOT_REACHED();
}
static Optional<Gfx::ExportFormat> texture_export_format(GLenum format, GLenum type)
{
switch (format) {
case GL_RGB:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::RGB888;
case GL_UNSIGNED_SHORT_5_6_5:
return Gfx::ExportFormat::RGB565;
default:
break;
}
break;
case GL_RGBA:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::RGBA8888;
case GL_UNSIGNED_SHORT_4_4_4_4:
// FIXME: This is not exactly the same as RGBA.
return Gfx::ExportFormat::RGBA4444;
case GL_UNSIGNED_SHORT_5_5_5_1:
return Gfx::ExportFormat::RGBA5551;
default:
break;
}
break;
case GL_ALPHA:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::Alpha8;
default:
break;
}
break;
case GL_LUMINANCE:
switch (type) {
case GL_UNSIGNED_BYTE:
return Gfx::ExportFormat::Gray8;
default:
break;
}
break;
default:
break;
}
dbgln("WebGL: Unsupported format and type combination. format: 0x{:04x}, type: 0x{:04x}", format, type);
return {};
}
HostWebGLContext::HostWebGLContext(NonnullOwnPtr<Web::WebGL::OpenGLContext> gl_context)
: m_gl_context(move(gl_context))
{
}
OwnPtr<HostWebGLContext> HostWebGLContext::create(NonnullRefPtr<Gfx::SkiaBackendContext> skia_backend_context, WebGLVersion version, Web::WebGL::OpenGLContext::DrawingBufferOptions options, Gfx::IntSize initial_size)
OwnPtr<HostWebGLContext> HostWebGLContext::create(NonnullRefPtr<Gfx::SkiaBackendContext> skia_backend_context, Web::WebGL::OpenGLContext::WebGLVersion version, Web::WebGL::OpenGLContext::DrawingBufferOptions options, Gfx::IntSize initial_size)
{
if (initial_size.width() < 1 || initial_size.width() > max_webgl_drawing_buffer_dimension
|| initial_size.height() < 1 || initial_size.height() > max_webgl_drawing_buffer_dimension)
return {};
auto gl_context = Web::WebGL::OpenGLContext::create(skia_backend_context, to_opengl_webgl_version(version), options);
auto gl_context = Web::WebGL::OpenGLContext::create(skia_backend_context, version, options);
if (!gl_context)
return {};
gl_context->set_size(initial_size);

View file

@ -30,7 +30,7 @@ namespace Compositor {
class HostWebGLContext {
public:
static OwnPtr<HostWebGLContext> create(NonnullRefPtr<Gfx::SkiaBackendContext>, Web::WebGL::WebGLVersion, Web::WebGL::OpenGLContext::DrawingBufferOptions, Gfx::IntSize initial_size);
static OwnPtr<HostWebGLContext> create(NonnullRefPtr<Gfx::SkiaBackendContext>, Web::WebGL::OpenGLContext::WebGLVersion, Web::WebGL::OpenGLContext::DrawingBufferOptions, Gfx::IntSize initial_size);
ErrorOr<void> execute_commands(ReadonlyBytes, Vector<Gfx::DecodedImageFrame> const& bitmaps);
ErrorOr<ByteBuffer> execute_sync_call(ReadonlyBytes request);

View file

@ -253,7 +253,7 @@ void PageClient::set_window_size(Web::DevicePixelSize size)
void PageClient::compositor_process_reconnected()
{
page().top_level_traversable()->repaint_after_compositor_process_reconnect();
page().republish_all_canvas_element_surfaces();
page().prepare_canvas_contexts_for_compositing();
page().update_all_media_element_video_sinks();
Web::HTML::main_thread_event_loop().queue_task_to_update_the_rendering();
}

View file

@ -28,51 +28,73 @@ public:
private:
virtual CreateResult create_context(Web::WebGL::WebGLVersion webgl_version, Gfx::IntSize initial_size, bool depth, bool stencil, bool antialias) override
{
VERIFY(!m_canvas_id.has_value());
CreateResult result;
auto canvas_id = m_connection->create_webgl_context(webgl_version, initial_size, depth, stencil, antialias, result.supported_extensions);
if (canvas_id.has_value()) {
result.success = true;
result.canvas_id = *canvas_id;
m_canvas_id = *canvas_id;
}
return result;
}
virtual void destroy_context(Web::Painting::CanvasId canvas_id) override
virtual Optional<Web::Painting::CanvasId> canvas_id() const override
{
m_connection->destroy_canvas_context(canvas_id);
return m_canvas_id;
}
virtual void send_commands(Web::Painting::CanvasId canvas_id, ByteBuffer const& commands, Vector<Gfx::DecodedImageFrame> const& bitmaps) override
virtual void destroy_context() override
{
m_connection->send_webgl_commands(canvas_id, commands, bitmaps);
if (!m_canvas_id.has_value())
return;
m_connection->destroy_canvas_context(*m_canvas_id);
m_canvas_id.clear();
}
virtual void present_canvas(Web::Painting::CanvasId canvas_id, bool preserve_drawing_buffer) override
virtual void send_commands(ByteBuffer const& commands, Vector<Gfx::DecodedImageFrame> const& bitmaps) override
{
m_connection->present_webgl_canvas(canvas_id, preserve_drawing_buffer);
if (!m_canvas_id.has_value())
return;
m_connection->send_webgl_commands(*m_canvas_id, commands, bitmaps);
}
virtual ByteBuffer sync_call(Web::Painting::CanvasId canvas_id, ByteBuffer request) override
virtual void present_canvas(bool preserve_drawing_buffer) override
{
return m_connection->webgl_sync_call(canvas_id, move(request));
if (!m_canvas_id.has_value())
return;
m_connection->present_webgl_canvas(*m_canvas_id, preserve_drawing_buffer);
}
virtual Web::WebGL::ReadPixelsResult read_pixels_robust_angle(Web::Painting::CanvasId canvas_id, Web::WebGL::GLint x, Web::WebGL::GLint y, Web::WebGL::GLsizei width, Web::WebGL::GLsizei height, Web::WebGL::GLenum format, Web::WebGL::GLenum type, Web::WebGL::GLsizei buf_size, Core::AnonymousBuffer pixels) override
virtual ByteBuffer sync_call(ByteBuffer request) override
{
return m_connection->read_webgl_pixels(canvas_id, x, y, width, height, format, type, buf_size, pixels);
if (!m_canvas_id.has_value())
return {};
return m_connection->webgl_sync_call(*m_canvas_id, move(request));
}
virtual void read_buffer_sub_data(Web::Painting::CanvasId canvas_id, Web::WebGL::GLenum target, Web::WebGL::GLintptr offset, Web::WebGL::GLintptr size, Core::AnonymousBuffer data) override
virtual Web::WebGL::ReadPixelsResult read_pixels_robust_angle(Web::WebGL::GLint x, Web::WebGL::GLint y, Web::WebGL::GLsizei width, Web::WebGL::GLsizei height, Web::WebGL::GLenum format, Web::WebGL::GLenum type, Web::WebGL::GLsizei buf_size, Core::AnonymousBuffer pixels) override
{
m_connection->read_webgl_buffer_sub_data(canvas_id, target, offset, size, data);
if (!m_canvas_id.has_value())
return {};
return m_connection->read_webgl_pixels(*m_canvas_id, x, y, width, height, format, type, buf_size, pixels);
}
virtual Gfx::ShareableBitmap read_back_drawing_buffer(Web::Painting::CanvasId canvas_id, Gfx::IntRect const& rect) override
virtual void read_buffer_sub_data(Web::WebGL::GLenum target, Web::WebGL::GLintptr offset, Web::WebGL::GLintptr size, Core::AnonymousBuffer data) override
{
return m_connection->get_canvas_pixels(canvas_id, rect);
if (!m_canvas_id.has_value())
return;
m_connection->read_webgl_buffer_sub_data(*m_canvas_id, target, offset, size, data);
}
virtual Gfx::ShareableBitmap read_back_drawing_buffer(Gfx::IntRect const& rect) override
{
if (!m_canvas_id.has_value())
return {};
return m_connection->get_canvas_pixels(*m_canvas_id, rect);
}
NonnullRefPtr<CompositorConnection> m_connection;
Optional<Web::Painting::CanvasId> m_canvas_id;
};
class WebContentRemoteCanvas2DTransport final : public Web::HTML::RemoteCanvas2DTransport {
@ -83,30 +105,48 @@ public:
}
private:
virtual Optional<Web::Painting::CanvasId> create_context(Gfx::IntSize size, bool alpha) override
virtual bool create_context(Gfx::IntSize size, bool alpha) override
{
return m_connection->create_canvas_2d_context(size, alpha);
VERIFY(!m_canvas_id.has_value());
auto canvas_id = m_connection->create_canvas_2d_context(size, alpha);
if (!canvas_id.has_value())
return false;
m_canvas_id = *canvas_id;
return true;
}
virtual void destroy_context(Web::Painting::CanvasId canvas_id) override
virtual Optional<Web::Painting::CanvasId> canvas_id() const override
{
m_connection->destroy_canvas_context(canvas_id);
return m_canvas_id;
}
virtual void update_commands(Web::Painting::CanvasId canvas_id, Gfx::CanvasCommandList const& commands) override
virtual void destroy_context() override
{
m_connection->update_canvas_2d_commands(canvas_id, commands);
if (!m_canvas_id.has_value())
return;
m_connection->destroy_canvas_context(*m_canvas_id);
m_canvas_id.clear();
}
virtual RefPtr<Gfx::Bitmap> read_back_pixels(Web::Painting::CanvasId canvas_id, Gfx::IntRect const& rect) override
virtual void update_commands(Gfx::CanvasCommandList const& commands) override
{
auto shareable_bitmap = m_connection->get_canvas_pixels(canvas_id, rect);
if (!m_canvas_id.has_value())
return;
m_connection->update_canvas_2d_commands(*m_canvas_id, commands);
}
virtual RefPtr<Gfx::Bitmap> read_back_pixels(Gfx::IntRect const& rect) override
{
if (!m_canvas_id.has_value())
return nullptr;
auto shareable_bitmap = m_connection->get_canvas_pixels(*m_canvas_id, rect);
if (!shareable_bitmap.is_valid())
return nullptr;
return shareable_bitmap.bitmap();
}
NonnullRefPtr<CompositorConnection> m_connection;
Optional<Web::Painting::CanvasId> m_canvas_id;
};
class WebContentCompositorHost final : public Web::Compositor::CompositorHost {

View file

@ -9,5 +9,5 @@ SaveLayer@0
CompositorWheelHitTestTarget@1 target_scroll_frame_index=0 rect=[8,8 784x32]
CompositorWheelHitTestTarget@1 target_scroll_frame_index=0 rect=[8,8 784x32]
CompositorWheelHitTestTarget@1 target_scroll_frame_index=0 rect=[8,8 32x32]
DrawCompositorSurface@1 dst_rect=[8,8 32x32]
DrawCanvas@1 dst_rect=[8,8 32x32]
Restore@0