From a80babffb60cf3f70fbdf1e8a8fa09f615f278ab Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Mon, 15 Jun 2026 19:37:51 +0200 Subject: [PATCH] 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. --- .../LibWeb/HTML/Canvas/AbstractCanvasMixin.h | 1 + .../LibWeb/HTML/Canvas/CanvasDrawImage.cpp | 12 +- .../LibWeb/HTML/Canvas/CanvasImageData.h | 2 +- .../HTML/Canvas/RemoteCanvas2DTransport.h | 9 +- .../LibWeb/HTML/CanvasRenderingContext2D.cpp | 143 ++++++--- .../LibWeb/HTML/CanvasRenderingContext2D.h | 25 +- Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp | 4 +- Libraries/LibWeb/HTML/HTMLCanvasElement.cpp | 167 ++++------ Libraries/LibWeb/HTML/HTMLCanvasElement.h | 21 +- Libraries/LibWeb/HTML/Navigable.cpp | 1 + .../OffscreenCanvasRenderingContext2D.cpp | 2 +- .../HTML/OffscreenCanvasRenderingContext2D.h | 2 +- .../LibWeb/HTML/TraversableNavigable.cpp | 1 + Libraries/LibWeb/Page/Page.cpp | 11 +- Libraries/LibWeb/Page/Page.h | 3 +- Libraries/LibWeb/Painting/CanvasPaintable.cpp | 11 +- Libraries/LibWeb/WebDriver/Screenshot.cpp | 15 +- .../WebGL/Extensions/ANGLEInstancedArrays.cpp | 2 +- .../WebGL/Extensions/EXTBlendMinMax.cpp | 2 +- .../WebGL/Extensions/EXTColorBufferFloat.cpp | 2 +- .../WebGL/Extensions/EXTRenderSnorm.cpp | 2 +- .../EXTTextureFilterAnisotropic.cpp | 2 +- .../WebGL/Extensions/EXTTextureNorm16.cpp | 2 +- .../WebGL/Extensions/OESElementIndexUint.cpp | 2 +- .../Extensions/OESStandardDerivatives.cpp | 2 +- .../WebGL/Extensions/OESVertexArrayObject.cpp | 45 +-- .../Extensions/WebGLCompressedTextureS3tc.cpp | 2 +- .../WebGLCompressedTextureS3tcSrgb.cpp | 2 +- .../WebGL/Extensions/WebGLDrawBuffers.cpp | 2 +- Libraries/LibWeb/WebGL/OpenGLContext.cpp | 60 +++- Libraries/LibWeb/WebGL/OpenGLContext.h | 11 +- Libraries/LibWeb/WebGL/RemoteWebGLTransport.h | 17 +- .../LibWeb/WebGL/WebGL2RenderingContext.cpp | 43 +-- .../LibWeb/WebGL/WebGL2RenderingContext.h | 11 +- .../WebGL/WebGL2RenderingContextImpl.cpp | 177 ++++++----- .../LibWeb/WebGL/WebGL2RenderingContextImpl.h | 2 +- .../WebGL/WebGL2RenderingContextOverloads.cpp | 47 +-- .../WebGL/WebGL2RenderingContextOverloads.h | 2 +- .../LibWeb/WebGL/WebGLContextProxyBase.cpp | 17 +- .../LibWeb/WebGL/WebGLContextProxyBase.h | 5 +- Libraries/LibWeb/WebGL/WebGLObject.cpp | 33 +- Libraries/LibWeb/WebGL/WebGLObject.h | 6 + .../LibWeb/WebGL/WebGLRenderingContext.cpp | 85 ++--- .../LibWeb/WebGL/WebGLRenderingContext.h | 15 +- .../WebGL/WebGLRenderingContextBase.cpp | 181 ++++------- .../LibWeb/WebGL/WebGLRenderingContextBase.h | 12 +- .../WebGL/WebGLRenderingContextImpl.cpp | 292 ++++++++++-------- .../LibWeb/WebGL/WebGLRenderingContextImpl.h | 10 +- .../WebGL/WebGLRenderingContextOverloads.cpp | 21 +- .../WebGL/WebGLRenderingContextOverloads.h | 2 +- Libraries/LibWeb/WebGL/WebGLSync.cpp | 16 +- Libraries/LibWeb/WebGL/WebGLSync.h | 1 + Services/Compositor/HostWebGLContext.cpp | 65 +--- Services/Compositor/HostWebGLContext.h | 2 +- Services/WebContent/PageClient.cpp | 2 +- .../WebContent/WebContentCompositorHost.cpp | 86 ++++-- .../canvas-compositor-surface.txt | 2 +- 57 files changed, 888 insertions(+), 832 deletions(-) diff --git a/Libraries/LibWeb/HTML/Canvas/AbstractCanvasMixin.h b/Libraries/LibWeb/HTML/Canvas/AbstractCanvasMixin.h index 8887bdb4e3..d422c00084 100644 --- a/Libraries/LibWeb/HTML/Canvas/AbstractCanvasMixin.h +++ b/Libraries/LibWeb/HTML/Canvas/AbstractCanvasMixin.h @@ -5,6 +5,7 @@ */ #include +#include #include #include #include diff --git a/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp b/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp index 56b04037c1..b368831b8f 100644 --- a/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp +++ b/Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp @@ -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 source) -> Gfx::IntSize { - if (auto painting_surface = source->surface()) - return painting_surface->size(); return { source->width(), source->height() }; }, [](GC::Ref source) -> Gfx::IntSize { @@ -67,11 +65,11 @@ Optional canvas_image_source_frame(CanvasImageSource con return image_data->frame(0, size); }, [](GC::Ref const& canvas) -> Optional { - 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> auto const& source) -> Optional { auto bitmap = source->bitmap(); diff --git a/Libraries/LibWeb/HTML/Canvas/CanvasImageData.h b/Libraries/LibWeb/HTML/Canvas/CanvasImageData.h index 8b08ad4e66..e10735852a 100644 --- a/Libraries/LibWeb/HTML/Canvas/CanvasImageData.h +++ b/Libraries/LibWeb/HTML/Canvas/CanvasImageData.h @@ -17,7 +17,7 @@ public: virtual WebIDL::ExceptionOr> create_image_data(int width, int height, Optional const& settings = {}) const = 0; virtual WebIDL::ExceptionOr> create_image_data(ImageData const&) const = 0; - virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) const = 0; + virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) = 0; virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y) = 0; virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) = 0; diff --git a/Libraries/LibWeb/HTML/Canvas/RemoteCanvas2DTransport.h b/Libraries/LibWeb/HTML/Canvas/RemoteCanvas2DTransport.h index e5bc3a2156..ca68ed96ff 100644 --- a/Libraries/LibWeb/HTML/Canvas/RemoteCanvas2DTransport.h +++ b/Libraries/LibWeb/HTML/Canvas/RemoteCanvas2DTransport.h @@ -20,11 +20,12 @@ class WEB_API RemoteCanvas2DTransport : public RefCounted 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 canvas_id() const = 0; + virtual void destroy_context() = 0; + virtual void update_commands(Gfx::CanvasCommandList const&) = 0; - virtual RefPtr read_back_pixels(Painting::CanvasId, Gfx::IntRect const&) = 0; + virtual RefPtr read_back_pixels(Gfx::IntRect const&) = 0; }; } diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 85862c1573..f2c4f483b6 100644 --- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -30,7 +29,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -40,16 +41,21 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include 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> 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(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 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 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()); + auto clipped_source = source_rect.intersected(source_bitmap_rect.to_type()); 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 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 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 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(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 max_width) @@ -585,7 +624,7 @@ WebIDL::ExceptionOr> CanvasRenderingContext2D::create_image_d } // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-getimagedata -WebIDL::ExceptionOr> CanvasRenderingContext2D::get_image_data(int x, int y, int width, int height, Optional const& settings) const +WebIDL::ExceptionOr> CanvasRenderingContext2D::get_image_data(int x, int y, int width, int height, Optional 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> 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> 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> 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(), snapshot, source_rect_intersected, Gfx::ScalingMode::NearestNeighbor, {}, 1, Gfx::CompositingAndBlendingOperator::SourceOver); + painter->draw_bitmap(image_data->bitmap().rect().to_type(), 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 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(), .color = clear_color() }); - } + if (canvas_command_list) + canvas_command_list->append(Gfx::CanvasCommands::ClearRect { .rect = Gfx::FloatRect { {}, m_size.to_type() }, .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()); + if (canvas_command_list) { + canvas_command_list->append(Gfx::CanvasCommands::Reset {}); + did_draw(Gfx::FloatRect { {}, m_size.to_type() }); } } diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h index 8c0bb70af9..47f1c6c2b9 100644 --- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h +++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h @@ -8,12 +8,13 @@ #pragma once +#include #include -#include #include #include #include #include +#include #include #include #include @@ -57,6 +58,8 @@ class CanvasRenderingContext2D GC_DECLARE_ALLOCATOR(CanvasRenderingContext2D); public: + static constexpr bool OVERRIDES_FINALIZE = true; + static JS::ThrowCompletionOr> create(JS::Realm&, HTMLCanvasElement&, JS::Value options); virtual ~CanvasRenderingContext2D() override; @@ -78,7 +81,7 @@ public: virtual WebIDL::ExceptionOr> create_image_data(int width, int height, Optional const& settings = {}) const override; virtual WebIDL::ExceptionOr> create_image_data(ImageData const& image_data) const override; - virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) const override; + virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) override; virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y) override; virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) override; WebIDL::ExceptionOr 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 surface(); - void allocate_painting_surface_if_needed(); + void ensure_backing_storage(); + + void discard_backing_storage(); + + Optional canvas_id() const; + + RefPtr 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 m_element; Gfx::CanvasCommandList m_commands; - OwnPtr m_player; + RefPtr m_transport; // https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-origin-clean bool m_origin_clean { true }; diff --git a/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp b/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp index ad2885cca8..348007f030 100644 --- a/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp +++ b/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp @@ -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()) { diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp index 46f010046a..5f9fd886e0 100644 --- a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include #include #include @@ -36,7 +38,13 @@ namespace Web::HTML { GC_DEFINE_ALLOCATOR(HTMLCanvasElement); -static constexpr auto max_canvas_area = 16384 * 16384; +static RefPtr 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& 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_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 quality = js_quality.has_value() && js_quality->is_number() ? js_quality->as_double() : Optional(); - 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 HTMLCanvasElement::to_blob(GC::Ref const& context) -> WebGL::WebGLRenderingContextBase* { return context.ptr(); }, + [](GC::Ref const& context) -> WebGL::WebGLRenderingContextBase* { return context.ptr(); }, + [](auto const&) -> WebGL::WebGLRenderingContextBase* { return nullptr; }); +} + +Optional 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 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 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& context) { - context->present(); + context->prepare_for_compositing(); }, [](GC::Ref& context) { - context->present(); + context->prepare_for_compositing(); }, [](GC::Ref& 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 HTMLCanvasElement::surface() const -{ - return m_context.visit( - [&](GC::Ref const& context) { - return context->surface(); - }, - [&](GC::Ref const& context) -> RefPtr { - return context->surface(); - }, - [&](GC::Ref const& context) -> RefPtr { - return context->surface(); - }, - [](Empty) -> RefPtr { - return {}; - }); -} - -void HTMLCanvasElement::allocate_painting_surface_if_needed() -{ - m_context.visit( - [&](GC::Ref& context) { - context->allocate_painting_surface_if_needed(); - }, - [&](GC::Ref& context) { - context->allocate_painting_surface_if_needed(); - }, - [&](GC::Ref& context) { - context->allocate_painting_surface_if_needed(); + context->prepare_for_compositing(); }, [](Empty) { // Do nothing. }); } +Optional 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(); +} + } diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.h b/Libraries/LibWeb/HTML/HTMLCanvasElement.h index f01abd52af..345bed1ae0 100644 --- a/Libraries/LibWeb/HTML/HTMLCanvasElement.h +++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.h @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -47,14 +46,20 @@ public: WebIDL::ExceptionOr to_blob(GC::Ref callback, StringView type, Optional quality); RefPtr get_bitmap_from_surface(); - void present(); - void republish_compositor_surface(); + void prepare_for_compositing(); void set_canvas_content_dirty(); + GC::Ptr canvas_rendering_context_2d() const + { + if (auto const* context = m_context.get_pointer>()) + return *context; + return nullptr; + } - RefPtr surface() const; - void allocate_painting_surface_if_needed(); + Optional canvas_id() const; - Painting::CompositorSurfaceId ensure_compositor_surface_id(); + Optional canvas_surface_content_size() const; + + void ensure_backing_storage(); CSS::ComputationContext canvas_font_computation_context(); @@ -73,13 +78,11 @@ private: template JS::ThrowCompletionOr 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, GC::Ref, Empty> m_context; - Optional m_compositor_surface_id; bool m_canvas_content_dirty { false }; }; diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp index 6c03293dce..705d61b5f9 100644 --- a/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Libraries/LibWeb/HTML/Navigable.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.cpp b/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.cpp index 0e028a8206..8b27bb5430 100644 --- a/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.cpp +++ b/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.cpp @@ -142,7 +142,7 @@ WebIDL::ExceptionOr> OffscreenCanvasRenderingContext2D::creat return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::create_image_data(ImageData&)"_utf16); } -WebIDL::ExceptionOr> OffscreenCanvasRenderingContext2D::get_image_data(int, int, int, int, Optional const&) const +WebIDL::ExceptionOr> OffscreenCanvasRenderingContext2D::get_image_data(int, int, int, int, Optional const&) { return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::get_image_data()"_utf16); } diff --git a/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.h b/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.h index 486ace3e6a..7bf5acde3a 100644 --- a/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.h +++ b/Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.h @@ -82,7 +82,7 @@ public: virtual WebIDL::ExceptionOr> create_image_data(int width, int height, Optional const& settings = {}) const override; virtual WebIDL::ExceptionOr> create_image_data(ImageData const& image_data) const override; - virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) const override; + virtual WebIDL::ExceptionOr> get_image_data(int x, int y, int width, int height, Optional const& settings = {}) override; virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y) override; virtual WebIDL::ExceptionOr put_image_data(ImageData&, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height) override; diff --git a/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Libraries/LibWeb/HTML/TraversableNavigable.cpp index 7abb847ba4..fa290bbd74 100644 --- a/Libraries/LibWeb/HTML/TraversableNavigable.cpp +++ b/Libraries/LibWeb/HTML/TraversableNavigable.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/Libraries/LibWeb/Page/Page.cpp b/Libraries/LibWeb/Page/Page.cpp index ba3fb12945..3f347f99f3 100644 --- a/Libraries/LibWeb/Page/Page.cpp +++ b/Libraries/LibWeb/Page/Page.cpp @@ -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(); }); } diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h index 202a813497..4012061050 100644 --- a/Libraries/LibWeb/Page/Page.h +++ b/Libraries/LibWeb/Page/Page.h @@ -233,8 +233,7 @@ public: void register_canvas_element(Badge, UniqueNodeID canvas_id); void unregister_canvas_element(Badge, 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; diff --git a/Libraries/LibWeb/Painting/CanvasPaintable.cpp b/Libraries/LibWeb/Painting/CanvasPaintable.cpp index 82261a1b97..498afdcc48 100644 --- a/Libraries/LibWeb/Painting/CanvasPaintable.cpp +++ b/Libraries/LibWeb/Painting/CanvasPaintable.cpp @@ -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(*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(); auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(), - surface->size(), canvas_int_rect.size()); - auto& mutable_canvas_element = const_cast(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); } } } diff --git a/Libraries/LibWeb/WebDriver/Screenshot.cpp b/Libraries/LibWeb/WebDriver/Screenshot.cpp index def83c2f39..ad8cdeb619 100644 --- a/Libraries/LibWeb/WebDriver/Screenshot.cpp +++ b/Libraries/LibWeb/WebDriver/Screenshot.cpp @@ -5,10 +5,13 @@ */ #include +#include #include #include #include +#include #include +#include #include #include #include @@ -46,9 +49,6 @@ ErrorOr, 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, 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, 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 element’s bitmap’s origin-clean flag is set to false, return error with error code unable to capture screen. // 2. If the canvas element’s 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 element’s bitmap as a file, using "image/png" as an argument. diff --git a/Libraries/LibWeb/WebGL/Extensions/ANGLEInstancedArrays.cpp b/Libraries/LibWeb/WebGL/Extensions/ANGLEInstancedArrays.cpp index ca76073e47..8045f6aa96 100644 --- a/Libraries/LibWeb/WebGL/Extensions/ANGLEInstancedArrays.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/ANGLEInstancedArrays.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/Libraries/LibWeb/WebGL/Extensions/EXTBlendMinMax.cpp b/Libraries/LibWeb/WebGL/Extensions/EXTBlendMinMax.cpp index 5c96c59c06..d8e1fc818b 100644 --- a/Libraries/LibWeb/WebGL/Extensions/EXTBlendMinMax.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/EXTBlendMinMax.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include namespace Web::WebGL { diff --git a/Libraries/LibWeb/WebGL/Extensions/EXTColorBufferFloat.cpp b/Libraries/LibWeb/WebGL/Extensions/EXTColorBufferFloat.cpp index 4dff63f4e3..6396bee8c4 100644 --- a/Libraries/LibWeb/WebGL/Extensions/EXTColorBufferFloat.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/EXTColorBufferFloat.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include namespace Web::WebGL { diff --git a/Libraries/LibWeb/WebGL/Extensions/EXTRenderSnorm.cpp b/Libraries/LibWeb/WebGL/Extensions/EXTRenderSnorm.cpp index 29c1a684ab..7c75f82ed8 100644 --- a/Libraries/LibWeb/WebGL/Extensions/EXTRenderSnorm.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/EXTRenderSnorm.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include namespace Web::WebGL { diff --git a/Libraries/LibWeb/WebGL/Extensions/EXTTextureFilterAnisotropic.cpp b/Libraries/LibWeb/WebGL/Extensions/EXTTextureFilterAnisotropic.cpp index 52356548ea..a5fd7e2eff 100644 --- a/Libraries/LibWeb/WebGL/Extensions/EXTTextureFilterAnisotropic.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/EXTTextureFilterAnisotropic.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include namespace Web::WebGL { diff --git a/Libraries/LibWeb/WebGL/Extensions/EXTTextureNorm16.cpp b/Libraries/LibWeb/WebGL/Extensions/EXTTextureNorm16.cpp index 2ed39c6192..f35388d450 100644 --- a/Libraries/LibWeb/WebGL/Extensions/EXTTextureNorm16.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/EXTTextureNorm16.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include namespace Web::WebGL { diff --git a/Libraries/LibWeb/WebGL/Extensions/OESElementIndexUint.cpp b/Libraries/LibWeb/WebGL/Extensions/OESElementIndexUint.cpp index 4f729446c6..d4a5b06ec2 100644 --- a/Libraries/LibWeb/WebGL/Extensions/OESElementIndexUint.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/OESElementIndexUint.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include namespace Web::WebGL { diff --git a/Libraries/LibWeb/WebGL/Extensions/OESStandardDerivatives.cpp b/Libraries/LibWeb/WebGL/Extensions/OESStandardDerivatives.cpp index 8cdc7334e5..5239a6f569 100644 --- a/Libraries/LibWeb/WebGL/Extensions/OESStandardDerivatives.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/OESStandardDerivatives.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include namespace Web::WebGL { diff --git a/Libraries/LibWeb/WebGL/Extensions/OESVertexArrayObject.cpp b/Libraries/LibWeb/WebGL/Extensions/OESVertexArrayObject.cpp index 99bbc8fa23..f51912d47b 100644 --- a/Libraries/LibWeb/WebGL/Extensions/OESVertexArrayObject.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/OESVertexArrayObject.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include @@ -43,33 +43,38 @@ void OESVertexArrayObject::delete_vertex_array_oes(GC::Ptrcontext().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 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 array_object) diff --git a/Libraries/LibWeb/WebGL/Extensions/WebGLCompressedTextureS3tc.cpp b/Libraries/LibWeb/WebGL/Extensions/WebGLCompressedTextureS3tc.cpp index 83ec625615..45ad574abb 100644 --- a/Libraries/LibWeb/WebGL/Extensions/WebGLCompressedTextureS3tc.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/WebGLCompressedTextureS3tc.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include namespace Web::WebGL { diff --git a/Libraries/LibWeb/WebGL/Extensions/WebGLCompressedTextureS3tcSrgb.cpp b/Libraries/LibWeb/WebGL/Extensions/WebGLCompressedTextureS3tcSrgb.cpp index 72a0bed331..1b8697817e 100644 --- a/Libraries/LibWeb/WebGL/Extensions/WebGLCompressedTextureS3tcSrgb.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/WebGLCompressedTextureS3tcSrgb.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include namespace Web::WebGL { diff --git a/Libraries/LibWeb/WebGL/Extensions/WebGLDrawBuffers.cpp b/Libraries/LibWeb/WebGL/Extensions/WebGLDrawBuffers.cpp index 1d6e95b04b..2b89933994 100644 --- a/Libraries/LibWeb/WebGL/Extensions/WebGLDrawBuffers.cpp +++ b/Libraries/LibWeb/WebGL/Extensions/WebGLDrawBuffers.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/Libraries/LibWeb/WebGL/OpenGLContext.cpp b/Libraries/LibWeb/WebGL/OpenGLContext.cpp index 00a2fd6324..1b4ea36f26 100644 --- a/Libraries/LibWeb/WebGL/OpenGLContext.cpp +++ b/Libraries/LibWeb/WebGL/OpenGLContext.cpp @@ -37,6 +37,56 @@ extern "C" { namespace Web::WebGL { +Optional 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 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 -} - } diff --git a/Libraries/LibWeb/WebGL/OpenGLContext.h b/Libraries/LibWeb/WebGL/OpenGLContext.h index 3a5a71e329..a18f4fefb2 100644 --- a/Libraries/LibWeb/WebGL/OpenGLContext.h +++ b/Libraries/LibWeb/WebGL/OpenGLContext.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,12 +19,11 @@ namespace Web::WebGL { +WEB_API Optional 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 get_supported_opengl_extensions(); - void request_extension(char const* extension_name); - - WebGLVersion webgl_version() const { return m_webgl_version; } private: NonnullRefPtr m_skia_backend_context; diff --git a/Libraries/LibWeb/WebGL/RemoteWebGLTransport.h b/Libraries/LibWeb/WebGL/RemoteWebGLTransport.h index 22744f04fd..d4d4b0044b 100644 --- a/Libraries/LibWeb/WebGL/RemoteWebGLTransport.h +++ b/Libraries/LibWeb/WebGL/RemoteWebGLTransport.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -27,19 +28,19 @@ public: struct CreateResult { bool success { false }; - Painting::CanvasId canvas_id { 0 }; Vector 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 canvas_id() const = 0; + virtual void destroy_context() = 0; - virtual void send_commands(Painting::CanvasId, ByteBuffer const&, Vector 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 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; }; } diff --git a/Libraries/LibWeb/WebGL/WebGL2RenderingContext.cpp b/Libraries/LibWeb/WebGL/WebGL2RenderingContext.cpp index 1e8656bbf1..780ac4937c 100644 --- a/Libraries/LibWeb/WebGL/WebGL2RenderingContext.cpp +++ b/Libraries/LibWeb/WebGL/WebGL2RenderingContext.cpp @@ -6,7 +6,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include #include @@ -15,9 +14,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -34,28 +33,16 @@ JS::ThrowCompletionOr> WebGL2RenderingContext::c // We should be coming here from getContext being called on a wrapped 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 { 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 { nullptr }; } - context->set_size(canvas_element.bitmap_size_for_canvas(1, 1)); - return realm.create(realm, canvas_element, context.release_nonnull(), context_attributes, context_attributes); } -WebGL2RenderingContext::WebGL2RenderingContext(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters) +WebGL2RenderingContext::WebGL2RenderingContext(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr 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 WebGL2RenderingContext::canvas_for_binding() const @@ -88,7 +75,7 @@ GC::Ref 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 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 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); } } diff --git a/Libraries/LibWeb/WebGL/WebGL2RenderingContext.h b/Libraries/LibWeb/WebGL/WebGL2RenderingContext.h index e91c9dc328..c145e5ab04 100644 --- a/Libraries/LibWeb/WebGL/WebGL2RenderingContext.h +++ b/Libraries/LibWeb/WebGL/WebGL2RenderingContext.h @@ -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 canvas_for_binding() const; + virtual GC::Ref canvas_for_binding() const override; Optional get_context_attributes(); - RefPtr 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 context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters); + WebGL2RenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters); virtual void visit_edges(Cell::Visitor&) override; diff --git a/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp b/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp index b09550d6bd..b10cd817cf 100644 --- a/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp +++ b/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.cpp @@ -16,10 +16,10 @@ extern "C" { #include #include #include -#include #include #include #include +#include #include #include #include @@ -36,7 +36,7 @@ extern "C" { namespace Web::WebGL { -WebGL2RenderingContextImpl::WebGL2RenderingContextImpl(JS::Realm& realm, NonnullOwnPtr context) +WebGL2RenderingContextImpl::WebGL2RenderingContextImpl(JS::Realm& realm, NonnullOwnPtr 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 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(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(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(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(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 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 query) @@ -744,17 +742,20 @@ void WebGL2RenderingContextImpl::delete_sampler(GC::Ptr 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 sampler) @@ -859,17 +860,19 @@ void WebGL2RenderingContextImpl::delete_sync(GC::Ptr 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(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(sync_handle.value())); } WebIDL::UnsignedLong WebGL2RenderingContextImpl::client_wait_sync(GC::Ref sync, WebIDL::UnsignedLong flags, WebIDL::UnsignedLongLong timeout) @@ -929,17 +932,20 @@ void WebGL2RenderingContextImpl::delete_transform_feedback(GC::Ptrmake_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 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(active_uniform_indices_buffer.data())); @@ -1228,17 +1234,20 @@ void WebGL2RenderingContextImpl::delete_vertex_array(GC::Ptrmake_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 { 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 array) diff --git a/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.h b/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.h index 580142ed46..14e93a6f7e 100644 --- a/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.h +++ b/Libraries/LibWeb/WebGL/WebGL2RenderingContextImpl.h @@ -24,7 +24,7 @@ class WebGL2RenderingContextImpl : public WebGLRenderingContextImpl { WEB_NON_IDL_PLATFORM_OBJECT(WebGL2RenderingContextImpl, WebGLRenderingContextImpl); public: - WebGL2RenderingContextImpl(JS::Realm&, NonnullOwnPtr); + WebGL2RenderingContextImpl(JS::Realm&, NonnullOwnPtr); 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); diff --git a/Libraries/LibWeb/WebGL/WebGL2RenderingContextOverloads.cpp b/Libraries/LibWeb/WebGL/WebGL2RenderingContextOverloads.cpp index d827ab3a17..3385f5193e 100644 --- a/Libraries/LibWeb/WebGL/WebGL2RenderingContextOverloads.cpp +++ b/Libraries/LibWeb/WebGL/WebGL2RenderingContextOverloads.cpp @@ -16,14 +16,14 @@ extern "C" { #include #include #include -#include #include +#include #include #include namespace Web::WebGL { -WebGL2RenderingContextOverloads::WebGL2RenderingContextOverloads(JS::Realm& realm, NonnullOwnPtr context) +WebGL2RenderingContextOverloads::WebGL2RenderingContextOverloads(JS::Realm& realm, NonnullOwnPtr 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(offset)); + m_context->read_pixels_into_pixel_pack_buffer(x, y, width, height, format, type, offset); } } diff --git a/Libraries/LibWeb/WebGL/WebGL2RenderingContextOverloads.h b/Libraries/LibWeb/WebGL/WebGL2RenderingContextOverloads.h index 1ff572c5cc..81ec6da328 100644 --- a/Libraries/LibWeb/WebGL/WebGL2RenderingContextOverloads.h +++ b/Libraries/LibWeb/WebGL/WebGL2RenderingContextOverloads.h @@ -23,7 +23,7 @@ class WebGL2RenderingContextOverloads : public WebGL2RenderingContextImpl { WEB_NON_IDL_PLATFORM_OBJECT(WebGL2RenderingContextOverloads, WebGL2RenderingContextImpl); public: - WebGL2RenderingContextOverloads(JS::Realm&, NonnullOwnPtr); + WebGL2RenderingContextOverloads(JS::Realm&, NonnullOwnPtr); 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); diff --git a/Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp index e7ac865e75..0bfe8e9562 100644 --- a/Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp +++ b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp @@ -13,9 +13,8 @@ namespace Web::WebGL { -WebGLContextProxyBase::WebGLContextProxyBase(NonnullRefPtr transport, Painting::CanvasId canvas_id, WebGLVersion webgl_version, Vector supported_extensions) +WebGLContextProxyBase::WebGLContextProxyBase(NonnullRefPtr transport, WebGLVersion webgl_version, Vector 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 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 WebGLContextProxyBase::read_back_drawing_buffer(Gfx::IntRect const& rect) @@ -65,7 +64,7 @@ RefPtr 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(offset), static_cast(destination.size()), shared_data); + m_transport->read_buffer_sub_data(target, static_cast(offset), static_cast(destination.size()), shared_data); if (m_lost) return; __builtin_memcpy(destination.data(), shared_data.data(), destination.size()); diff --git a/Libraries/LibWeb/WebGL/WebGLContextProxyBase.h b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.h index eb18264dc5..6f7d66430a 100644 --- a/Libraries/LibWeb/WebGL/WebGLContextProxyBase.h +++ b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.h @@ -31,11 +31,11 @@ class WEB_API WebGLContextProxyBase { AK_MAKE_NONMOVABLE(WebGLContextProxyBase); public: - WebGLContextProxyBase(NonnullRefPtr, Painting::CanvasId, WebGLVersion, Vector supported_extensions); + WebGLContextProxyBase(NonnullRefPtr, WebGLVersion, Vector supported_extensions); ~WebGLContextProxyBase(); void flush_commands(); - Painting::CanvasId canvas_id() const { return m_canvas_id; } + Optional canvas_id() const { return m_transport->canvas_id(); } void make_current() { } void notify_content_will_change() { } @@ -91,7 +91,6 @@ protected: private: NonnullRefPtr m_transport; - Painting::CanvasId m_canvas_id { 0 }; WebGLVersion m_webgl_version { WebGLVersion::WebGL1 }; Vector m_supported_extensions; WebGLCommandList m_commands; diff --git a/Libraries/LibWeb/WebGL/WebGLObject.cpp b/Libraries/LibWeb/WebGL/WebGLObject.cpp index 6183fe42f6..298176429e 100644 --- a/Libraries/LibWeb/WebGL/WebGLObject.cpp +++ b/Libraries/LibWeb/WebGL/WebGLObject.cpp @@ -36,9 +36,40 @@ void WebGLObject::visit_edges(Visitor& visitor) } ErrorOr 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> WebGLObject::handle_for_deletion(WebGLRenderingContextBase const* context) +{ + TRY(validate_context(context)); + if (invalidated_for_context(context)) + return Optional {}; + m_invalidated = true; + return Optional { m_handle }; +} + +ErrorOr> WebGLObject::handle_for_query(WebGLRenderingContextBase const* context) const +{ + TRY(validate_context(context)); + if (invalidated_for_context(context)) + return Optional {}; + return Optional { m_handle }; +} + +bool WebGLObject::invalidated_for_context(WebGLRenderingContextBase const* context) const +{ + (void)context; + return m_invalidated; +} + +ErrorOr WebGLObject::validate_context(WebGLRenderingContextBase const* context) const { if (context == m_context) - return m_handle; + return {}; return Error::from_errno(GL_INVALID_OPERATION); } diff --git a/Libraries/LibWeb/WebGL/WebGLObject.h b/Libraries/LibWeb/WebGL/WebGLObject.h index b2a16e0c5c..1fc15f74a8 100644 --- a/Libraries/LibWeb/WebGL/WebGLObject.h +++ b/Libraries/LibWeb/WebGL/WebGLObject.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -25,6 +26,8 @@ public: void set_label(String const& label) { m_label = label; } ErrorOr handle(WebGLRenderingContextBase const* context) const; + ErrorOr> handle_for_deletion(WebGLRenderingContextBase const* context); + ErrorOr> handle_for_query(WebGLRenderingContextBase const* context) const; protected: explicit WebGLObject(JS::Realm&, GC::Ref, 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 validate_context(WebGLRenderingContextBase const* context) const; GC::Ref m_context; diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp b/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp index df8342f6ec..4cc8d1b9bd 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp @@ -5,18 +5,22 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include #include #include #include +#include +#include #include +#include #include +#include #include #include -#include +#include #include +#include #include #include #include @@ -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 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(transport.release_nonnull(), webgl_version, move(result.supported_extensions)); +} + JS::ThrowCompletionOr> WebGLRenderingContext::create(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, JS::Value options) { // We should be coming here from getContext being called on a wrapped 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 { 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 { nullptr }; } - context->set_size(canvas_element.bitmap_size_for_canvas(1, 1)); - return realm.create(realm, canvas_element, context.release_nonnull(), context_attributes, context_attributes); } -WebGLRenderingContext::WebGLRenderingContext(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters) +WebGLRenderingContext::WebGLRenderingContext(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr 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 WebGLRenderingContext::canvas_for_binding() const @@ -105,7 +128,7 @@ GC::Ref 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 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 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); } } diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContext.h b/Libraries/LibWeb/WebGL/WebGLRenderingContext.h index 8de41c548d..8b710efed9 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContext.h +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContext.h @@ -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 canvas_for_binding() const; + virtual GC::Ref canvas_for_binding() const override; Optional get_context_attributes(); - RefPtr 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 context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters); + WebGLRenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr 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 create_webgl_context_proxy(HTML::HTMLCanvasElement&, WebGLVersion, WebGLContextAttributes const&); + } diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp index ec7c044ffe..39f11aaa77 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp @@ -11,9 +11,7 @@ extern "C" { #include } -#include #include -#include #include #include #include @@ -24,6 +22,7 @@ extern "C" { #include #include #include +#include #include #include #include @@ -38,68 +37,12 @@ extern "C" { #include #include #include +#include +#include #include -#include -#include -#include -#include -#include -#include - namespace Web::WebGL { -static constexpr Optional 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 required_angle_extensions; JS::ThrowCompletionOr> (*factory)(JS::Realm&, GC::Ref); - Optional only_for_webgl_version { OptionalNone {} }; + Optional only_for_webgl_version { OptionalNone {} }; }; static HashMap const& available_webgl_extensions() { static auto const& extensions = *new HashMap { // 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 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> WebGLRenderingContextBase::get_supported_extensions() { - auto opengl_extensions = context().get_supported_opengl_extensions(); + auto const& opengl_extensions = context().get_supported_opengl_extensions(); Vector 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 WebGLRenderingContextBase::enabled_compressed return m_enabled_compressed_texture_formats; } -Optional WebGLRenderingContextBase::read_and_pixel_convert_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Optional destination_width, Optional destination_height) +Optional 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 WebGLRenderingContextBase::read_and_pixel_conv return source->current_image_frame(); }, [](GC::Ref source) -> Optional { - 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 source) -> Optional { return Gfx::DecodedImageFrame { *source->bitmap() }; @@ -293,41 +233,34 @@ Optional 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>()) + 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>(), + .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; } diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h index 124586daa8..ac5f3c3ab4 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h @@ -6,7 +6,7 @@ #pragma once -#include +#include #include #include #include @@ -45,7 +45,8 @@ public: using Int32List = Variant, Vector>; using Uint32List = Variant, Vector>; - virtual OpenGLContext& context() = 0; + virtual WebGLContextProxy& context() = 0; + virtual GC::Ref 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 read_and_pixel_convert_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type, Optional destination_width = OptionalNone {}, Optional destination_height = OptionalNone {}); + struct TexImageSourceFrame { + Gfx::DecodedImageFrame frame; + bool flip_y { false }; + bool premultiply_alpha { false }; + }; + Optional read_texture_image_source(TexImageSource const& source, WebIDL::UnsignedLong format, WebIDL::UnsignedLong type); static Vector null_terminated_string(StringView string) { diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp index ab72ff46ab..6a5cdd2145 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp @@ -16,9 +16,9 @@ extern "C" { #include #include #include -#include #include #include +#include #include #include #include @@ -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 context) +WebGLRenderingContextImpl::WebGLRenderingContextImpl(JS::Realm& realm, NonnullOwnPtr 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 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 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 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 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 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 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(offset)); - needs_to_present(); + did_update_canvas_content(); } void WebGLRenderingContextImpl::enable(WebIDL::UnsignedLong cap) @@ -1268,7 +1285,7 @@ WebIDL::ExceptionOr 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 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 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 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 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 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 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 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 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 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 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 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) diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h index 28e4860b00..4c27e0e704 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h @@ -24,12 +24,12 @@ class WebGLRenderingContextImpl : public WebGLRenderingContextBase { WEB_NON_IDL_PLATFORM_OBJECT(WebGLRenderingContextImpl, WebGLRenderingContextBase); public: - WebGLRenderingContextImpl(JS::Realm&, NonnullOwnPtr); + WebGLRenderingContextImpl(JS::Realm&, NonnullOwnPtr); - 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 program, GC::Ref shader); @@ -171,7 +171,7 @@ protected: GC::Ptr m_any_samples_passed_conservative; GC::Ptr m_transform_feedback_primitives_written; - NonnullOwnPtr m_context; + NonnullOwnPtr m_context; }; } diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextOverloads.cpp b/Libraries/LibWeb/WebGL/WebGLRenderingContextOverloads.cpp index ee8b8309e6..f36fe913b6 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextOverloads.cpp +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextOverloads.cpp @@ -15,13 +15,13 @@ extern "C" { #include #include #include -#include +#include #include #include namespace Web::WebGL { -WebGLRenderingContextOverloads::WebGLRenderingContextOverloads(JS::Realm& realm, NonnullOwnPtr context) +WebGLRenderingContextOverloads::WebGLRenderingContextOverloads(JS::Realm& realm, NonnullOwnPtr 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 location, Float32List v) diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextOverloads.h b/Libraries/LibWeb/WebGL/WebGLRenderingContextOverloads.h index 772672fc41..0fcc85ccd7 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextOverloads.h +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextOverloads.h @@ -23,7 +23,7 @@ class WebGLRenderingContextOverloads : public WebGLRenderingContextImpl { WEB_NON_IDL_PLATFORM_OBJECT(WebGLRenderingContextOverloads, WebGLRenderingContextImpl); public: - WebGLRenderingContextOverloads(JS::Realm&, NonnullOwnPtr); + WebGLRenderingContextOverloads(JS::Realm&, NonnullOwnPtr); void buffer_data(WebIDL::UnsignedLong target, WebIDL::LongLong size, WebIDL::UnsignedLong usage); void buffer_data(WebIDL::UnsignedLong target, WebIDL::NullableBufferSourceVariant data, WebIDL::UnsignedLong usage); diff --git a/Libraries/LibWeb/WebGL/WebGLSync.cpp b/Libraries/LibWeb/WebGL/WebGLSync.cpp index 798e37e089..95cf11e9a4 100644 --- a/Libraries/LibWeb/WebGL/WebGLSync.cpp +++ b/Libraries/LibWeb/WebGL/WebGLSync.cpp @@ -36,9 +36,19 @@ void WebGLSync::initialize(JS::Realm& realm) ErrorOr 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> WebGLSync::sync_handle_for_deletion(WebGLRenderingContextBase const* context) +{ + TRY(validate_context(context)); + if (invalidated_for_context(context)) + return Optional {}; + invalidate(); + return Optional { m_sync_handle }; } } diff --git a/Libraries/LibWeb/WebGL/WebGLSync.h b/Libraries/LibWeb/WebGL/WebGLSync.h index 9772876e8d..a165c19c98 100644 --- a/Libraries/LibWeb/WebGL/WebGLSync.h +++ b/Libraries/LibWeb/WebGL/WebGLSync.h @@ -21,6 +21,7 @@ public: virtual ~WebGLSync() override; ErrorOr sync_handle(WebGLRenderingContextBase const* context) const; + ErrorOr> sync_handle_for_deletion(WebGLRenderingContextBase const* context); protected: explicit WebGLSync(JS::Realm&, GC::Ref, GLsyncInternal handle); diff --git a/Services/Compositor/HostWebGLContext.cpp b/Services/Compositor/HostWebGLContext.cpp index c22167a434..028569f408 100644 --- a/Services/Compositor/HostWebGLContext.cpp +++ b/Services/Compositor/HostWebGLContext.cpp @@ -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 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 gl_context) : m_gl_context(move(gl_context)) { } -OwnPtr HostWebGLContext::create(NonnullRefPtr skia_backend_context, WebGLVersion version, Web::WebGL::OpenGLContext::DrawingBufferOptions options, Gfx::IntSize initial_size) +OwnPtr HostWebGLContext::create(NonnullRefPtr 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); diff --git a/Services/Compositor/HostWebGLContext.h b/Services/Compositor/HostWebGLContext.h index 31580a0185..a0e0038a0f 100644 --- a/Services/Compositor/HostWebGLContext.h +++ b/Services/Compositor/HostWebGLContext.h @@ -30,7 +30,7 @@ namespace Compositor { class HostWebGLContext { public: - static OwnPtr create(NonnullRefPtr, Web::WebGL::WebGLVersion, Web::WebGL::OpenGLContext::DrawingBufferOptions, Gfx::IntSize initial_size); + static OwnPtr create(NonnullRefPtr, Web::WebGL::OpenGLContext::WebGLVersion, Web::WebGL::OpenGLContext::DrawingBufferOptions, Gfx::IntSize initial_size); ErrorOr execute_commands(ReadonlyBytes, Vector const& bitmaps); ErrorOr execute_sync_call(ReadonlyBytes request); diff --git a/Services/WebContent/PageClient.cpp b/Services/WebContent/PageClient.cpp index 2f54005f15..c91e507247 100644 --- a/Services/WebContent/PageClient.cpp +++ b/Services/WebContent/PageClient.cpp @@ -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(); } diff --git a/Services/WebContent/WebContentCompositorHost.cpp b/Services/WebContent/WebContentCompositorHost.cpp index 45b0cb927c..c566db0ba5 100644 --- a/Services/WebContent/WebContentCompositorHost.cpp +++ b/Services/WebContent/WebContentCompositorHost.cpp @@ -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 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 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 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 m_connection; + Optional m_canvas_id; }; class WebContentRemoteCanvas2DTransport final : public Web::HTML::RemoteCanvas2DTransport { @@ -83,30 +105,48 @@ public: } private: - virtual Optional 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 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 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 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 m_connection; + Optional m_canvas_id; }; class WebContentCompositorHost final : public Web::Compositor::CompositorHost { diff --git a/Tests/LibWeb/Text/expected/display_list/canvas-compositor-surface.txt b/Tests/LibWeb/Text/expected/display_list/canvas-compositor-surface.txt index 7fabe328cf..1064033878 100644 --- a/Tests/LibWeb/Text/expected/display_list/canvas-compositor-surface.txt +++ b/Tests/LibWeb/Text/expected/display_list/canvas-compositor-surface.txt @@ -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