LibWeb+WebContent: Restore canvas contexts after Compositor loss
Compositor-backed canvas contexts keep their transports tied to a single Compositor connection. When that connection dies, the 2D backing storage and WebGL GL objects disappear with it, but WebContent does not surface the loss to canvas contexts or create fresh host contexts after reconnect. Track compositor loss through the WebContent connection, mark WebGL contexts lost, dispatch the standard context events, and rebuild the remote proxy when the page opts into restoration. For 2D canvas, queue the canvas context loss steps, discard the dead backing storage, and create new storage before firing contextrestored.
This commit is contained in:
parent
a80babffb6
commit
f215d9bb9a
26 changed files with 275 additions and 6 deletions
|
|
@ -42,6 +42,8 @@ public:
|
|||
protected:
|
||||
CanvasState() = default;
|
||||
|
||||
void set_context_lost(bool context_lost) { m_context_lost = context_lost; }
|
||||
|
||||
private:
|
||||
DrawingState m_drawing_state;
|
||||
Vector<DrawingState> m_drawing_state_stack;
|
||||
|
|
|
|||
|
|
@ -31,9 +31,12 @@
|
|||
#include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h>
|
||||
#include <LibWeb/Compositor/CompositorHost.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Event.h>
|
||||
#include <LibWeb/HTML/Canvas/RemoteCanvas2DTransport.h>
|
||||
#include <LibWeb/HTML/CanvasRenderingContext2D.h>
|
||||
#include <LibWeb/HTML/DecodedImageData.h>
|
||||
#include <LibWeb/HTML/EventLoop/EventLoop.h>
|
||||
#include <LibWeb/HTML/EventNames.h>
|
||||
#include <LibWeb/HTML/HTMLCanvasElement.h>
|
||||
#include <LibWeb/HTML/HTMLImageElement.h>
|
||||
#include <LibWeb/HTML/HTMLMediaElement.h>
|
||||
|
|
@ -43,6 +46,7 @@
|
|||
#include <LibWeb/HTML/ImageRequest.h>
|
||||
#include <LibWeb/HTML/Navigable.h>
|
||||
#include <LibWeb/HTML/Path2D.h>
|
||||
#include <LibWeb/HTML/Scripting/Environments.h>
|
||||
#include <LibWeb/HTML/TextMetrics.h>
|
||||
#include <LibWeb/Infra/CharacterTypes.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
|
|
@ -323,6 +327,54 @@ Optional<Painting::CanvasId> CanvasRenderingContext2D::canvas_id() const
|
|||
return m_transport->canvas_id();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#context-loss
|
||||
void CanvasRenderingContext2D::notify_backing_storage_lost()
|
||||
{
|
||||
if (!has_backing_storage())
|
||||
return;
|
||||
|
||||
// When the user agent detects that the backing storage associated with a canvas context has been lost, then it
|
||||
// must queue a global task on the DOM manipulation task source given canvas's relevant global object to run
|
||||
// these steps:
|
||||
queue_global_task(HTML::Task::Source::DOMManipulation, relevant_global_object(*this), GC::create_function(heap(), [this] {
|
||||
// 1. Let canvas be context's canvas element.
|
||||
// 2. If context's context lost is true, then abort these steps.
|
||||
if (is_context_lost())
|
||||
return;
|
||||
|
||||
// 3. Set context's context lost to true.
|
||||
set_context_lost(true);
|
||||
|
||||
// AD-HOC: Drop recorded-but-unflushed draw commands; they targeted the lost storage.
|
||||
discard_backing_storage();
|
||||
|
||||
// 4. Reset the rendering context to its default state given context.
|
||||
reset_to_default_state();
|
||||
|
||||
// 5. Let shouldRestore be the result of firing an event named contextlost at canvas, with the cancelable
|
||||
// attribute initialized to true.
|
||||
Bindings::EventInit context_lost_event_init;
|
||||
context_lost_event_init.cancelable = true;
|
||||
bool should_restore = m_element->dispatch_event(DOM::Event::create(realm(), HTML::EventNames::contextlost, context_lost_event_init));
|
||||
|
||||
// 6. If shouldRestore is false, then abort these steps.
|
||||
if (!should_restore)
|
||||
return;
|
||||
|
||||
// 7. Attempt to restore context by creating a backing storage using context's attributes and associating
|
||||
// them with context. If this fails, then abort these steps.
|
||||
ensure_backing_storage();
|
||||
if (!has_backing_storage())
|
||||
return;
|
||||
|
||||
// 8. Set context's context lost to false.
|
||||
set_context_lost(false);
|
||||
|
||||
// 9. Fire an event named contextrestored at canvas.
|
||||
m_element->dispatch_event(DOM::Event::create(realm(), HTML::EventNames::contextrestored));
|
||||
}));
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::ensure_backing_storage()
|
||||
{
|
||||
if (has_backing_storage() || m_size.is_empty())
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ public:
|
|||
|
||||
void discard_backing_storage();
|
||||
|
||||
void notify_backing_storage_lost();
|
||||
|
||||
Optional<Painting::CanvasId> canvas_id() const;
|
||||
|
||||
RefPtr<Gfx::Bitmap> read_pixels(Gfx::IntRect const&);
|
||||
|
|
|
|||
|
|
@ -441,6 +441,12 @@ RefPtr<Gfx::Bitmap> HTMLCanvasElement::get_bitmap_from_surface()
|
|||
return bitmap;
|
||||
}
|
||||
|
||||
void HTMLCanvasElement::notify_compositor_connection_lost()
|
||||
{
|
||||
if (auto* webgl_context = this->webgl_context())
|
||||
webgl_context->lose_context_from_compositor_loss();
|
||||
}
|
||||
|
||||
void HTMLCanvasElement::set_canvas_content_dirty()
|
||||
{
|
||||
m_canvas_content_dirty = true;
|
||||
|
|
@ -467,6 +473,16 @@ void HTMLCanvasElement::prepare_for_compositing()
|
|||
});
|
||||
}
|
||||
|
||||
void HTMLCanvasElement::notify_compositor_backing_storage_lost()
|
||||
{
|
||||
if (auto* webgl_context = this->webgl_context()) {
|
||||
webgl_context->restore_context_after_compositor_reconnect();
|
||||
return;
|
||||
}
|
||||
if (auto context_2d = canvas_rendering_context_2d())
|
||||
context_2d->notify_backing_storage_lost();
|
||||
}
|
||||
|
||||
Optional<Gfx::IntSize> HTMLCanvasElement::canvas_surface_content_size() const
|
||||
{
|
||||
if (!canvas_id().has_value())
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ public:
|
|||
RefPtr<Gfx::Bitmap> get_bitmap_from_surface();
|
||||
|
||||
void prepare_for_compositing();
|
||||
void notify_compositor_backing_storage_lost();
|
||||
void set_canvas_content_dirty();
|
||||
GC::Ptr<HTML::CanvasRenderingContext2D> canvas_rendering_context_2d() const
|
||||
{
|
||||
|
|
@ -61,6 +62,8 @@ public:
|
|||
|
||||
void ensure_backing_storage();
|
||||
|
||||
void notify_compositor_connection_lost();
|
||||
|
||||
CSS::ComputationContext canvas_font_computation_context();
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -723,6 +723,20 @@ void Page::prepare_canvas_contexts_for_compositing()
|
|||
});
|
||||
}
|
||||
|
||||
void Page::notify_all_canvas_elements_of_lost_backing_storage()
|
||||
{
|
||||
for_each_canvas_element([](auto& canvas_element) {
|
||||
canvas_element.notify_compositor_backing_storage_lost();
|
||||
});
|
||||
}
|
||||
|
||||
void Page::notify_all_webgl_contexts_lost()
|
||||
{
|
||||
for_each_canvas_element([](auto& canvas_element) {
|
||||
canvas_element.notify_compositor_connection_lost();
|
||||
});
|
||||
}
|
||||
|
||||
void Page::did_request_media_context_menu(UniqueNodeID media_id, CSSPixelPoint position, ByteString const& target, unsigned modifiers, MediaContextMenu const& menu)
|
||||
{
|
||||
m_media_context_menu_element_id = media_id;
|
||||
|
|
|
|||
|
|
@ -234,6 +234,8 @@ public:
|
|||
void unregister_canvas_element(Badge<HTML::HTMLCanvasElement>, UniqueNodeID canvas_id);
|
||||
|
||||
void prepare_canvas_contexts_for_compositing();
|
||||
void notify_all_canvas_elements_of_lost_backing_storage();
|
||||
void notify_all_webgl_contexts_lost();
|
||||
|
||||
struct MediaContextMenu {
|
||||
URL::URL media_url;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,11 @@ void WebGL2RenderingContext::prepare_for_compositing()
|
|||
context().present_canvas_for_compositing(m_context_creation_parameters.preserve_drawing_buffer);
|
||||
}
|
||||
|
||||
bool WebGL2RenderingContext::reestablish_remote_context()
|
||||
{
|
||||
return restore_webgl_context_proxy(context(), *m_canvas_element, WebGLVersion::WebGL2, m_actual_context_parameters);
|
||||
}
|
||||
|
||||
GC::Ref<HTML::HTMLCanvasElement> WebGL2RenderingContext::canvas_for_binding() const
|
||||
{
|
||||
return *m_canvas_element;
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ private:
|
|||
WebGL2RenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr<WebGLContextProxy> context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
virtual bool reestablish_remote_context() override;
|
||||
|
||||
GC::Ref<HTML::HTMLCanvasElement> m_canvas_element;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,16 @@ WebGLContextProxyBase::~WebGLContextProxyBase()
|
|||
m_transport->destroy_context();
|
||||
}
|
||||
|
||||
void WebGLContextProxyBase::restore(NonnullRefPtr<RemoteWebGLTransport> transport, Vector<String> supported_extensions)
|
||||
{
|
||||
m_transport = move(transport);
|
||||
m_supported_extensions = move(supported_extensions);
|
||||
m_lost = false;
|
||||
m_commands.clear_with_capacity();
|
||||
m_pending_bitmaps.clear_with_capacity();
|
||||
m_string_cache.clear();
|
||||
}
|
||||
|
||||
void WebGLContextProxyBase::flush_commands()
|
||||
{
|
||||
if (m_commands.is_empty())
|
||||
|
|
|
|||
|
|
@ -35,8 +35,11 @@ public:
|
|||
~WebGLContextProxyBase();
|
||||
|
||||
void flush_commands();
|
||||
void set_lost() { m_lost = true; }
|
||||
Optional<Painting::CanvasId> canvas_id() const { return m_transport->canvas_id(); }
|
||||
|
||||
void restore(NonnullRefPtr<RemoteWebGLTransport>, Vector<String> supported_extensions);
|
||||
|
||||
void make_current() { }
|
||||
void notify_content_will_change() { }
|
||||
u32 default_framebuffer() const { return 0; }
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ WebGLObject::WebGLObject(JS::Realm& realm, GC::Ref<WebGLRenderingContextBase> co
|
|||
: Bindings::PlatformObject(realm)
|
||||
, m_context(context)
|
||||
, m_handle(handle)
|
||||
, m_context_generation(context->context_generation())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -62,8 +63,7 @@ ErrorOr<Optional<GLuint>> WebGLObject::handle_for_query(WebGLRenderingContextBas
|
|||
|
||||
bool WebGLObject::invalidated_for_context(WebGLRenderingContextBase const* context) const
|
||||
{
|
||||
(void)context;
|
||||
return m_invalidated;
|
||||
return m_invalidated || m_context_generation != context->context_generation();
|
||||
}
|
||||
|
||||
ErrorOr<void> WebGLObject::validate_context(WebGLRenderingContextBase const* context) const
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ protected:
|
|||
|
||||
private:
|
||||
GLuint m_handle { 0 };
|
||||
u64 m_context_generation { 0 };
|
||||
|
||||
bool m_invalidated { false };
|
||||
String m_label;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ namespace Web::WebGL {
|
|||
|
||||
GC_DEFINE_ALLOCATOR(WebGLRenderingContext);
|
||||
|
||||
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#fire-a-webgl-context-event
|
||||
// Returns false if the event was canceled (the page called preventDefault), which is how
|
||||
// webglcontextlost signals that the page wants the context restored.
|
||||
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.
|
||||
|
|
@ -60,7 +63,14 @@ static Gfx::IntSize initial_drawing_buffer_size(HTML::HTMLCanvasElement& canvas_
|
|||
};
|
||||
}
|
||||
|
||||
OwnPtr<WebGLContextProxy> create_webgl_context_proxy(HTML::HTMLCanvasElement& canvas_element, WebGLVersion webgl_version, WebGLContextAttributes const& context_attributes)
|
||||
namespace {
|
||||
|
||||
struct RemoteWebGLContext {
|
||||
NonnullRefPtr<RemoteWebGLTransport> transport;
|
||||
RemoteWebGLTransport::CreateResult result;
|
||||
};
|
||||
|
||||
Optional<RemoteWebGLContext> create_remote_webgl_context(HTML::HTMLCanvasElement& canvas_element, WebGLVersion webgl_version, WebGLContextAttributes const& context_attributes)
|
||||
{
|
||||
auto& page = canvas_element.document().page();
|
||||
if (!page.has_compositor_host())
|
||||
|
|
@ -78,7 +88,28 @@ OwnPtr<WebGLContextProxy> create_webgl_context_proxy(HTML::HTMLCanvasElement& ca
|
|||
if (!result.success)
|
||||
return {};
|
||||
|
||||
return make<WebGLContextProxy>(transport.release_nonnull(), webgl_version, move(result.supported_extensions));
|
||||
return RemoteWebGLContext { transport.release_nonnull(), move(result) };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OwnPtr<WebGLContextProxy> create_webgl_context_proxy(HTML::HTMLCanvasElement& canvas_element, WebGLVersion webgl_version, WebGLContextAttributes const& context_attributes)
|
||||
{
|
||||
auto remote = create_remote_webgl_context(canvas_element, webgl_version, context_attributes);
|
||||
if (!remote.has_value())
|
||||
return {};
|
||||
|
||||
return make<WebGLContextProxy>(move(remote->transport), webgl_version, move(remote->result.supported_extensions));
|
||||
}
|
||||
|
||||
bool restore_webgl_context_proxy(WebGLContextProxy& context, HTML::HTMLCanvasElement& canvas_element, WebGLVersion webgl_version, WebGLContextAttributes const& context_attributes)
|
||||
{
|
||||
auto remote = create_remote_webgl_context(canvas_element, webgl_version, context_attributes);
|
||||
if (!remote.has_value())
|
||||
return false;
|
||||
|
||||
context.restore(move(remote->transport), move(remote->result.supported_extensions));
|
||||
return true;
|
||||
}
|
||||
|
||||
JS::ThrowCompletionOr<GC::Ptr<WebGLRenderingContext>> WebGLRenderingContext::create(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, JS::Value options)
|
||||
|
|
@ -123,6 +154,11 @@ void WebGLRenderingContext::prepare_for_compositing()
|
|||
context().present_canvas_for_compositing(m_context_creation_parameters.preserve_drawing_buffer);
|
||||
}
|
||||
|
||||
bool WebGLRenderingContext::reestablish_remote_context()
|
||||
{
|
||||
return restore_webgl_context_proxy(context(), *m_canvas_element, WebGLVersion::WebGL1, m_actual_context_parameters);
|
||||
}
|
||||
|
||||
GC::Ref<HTML::HTMLCanvasElement> WebGLRenderingContext::canvas_for_binding() const
|
||||
{
|
||||
return *m_canvas_element;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ private:
|
|||
WebGLRenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr<WebGLContextProxy> context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
virtual bool reestablish_remote_context() override;
|
||||
|
||||
GC::Ref<HTML::HTMLCanvasElement> m_canvas_element;
|
||||
|
||||
|
|
@ -60,5 +61,6 @@ bool fire_webgl_context_event(HTML::HTMLCanvasElement& canvas_element, FlyString
|
|||
void fire_webgl_context_creation_error(HTML::HTMLCanvasElement& canvas_element);
|
||||
|
||||
OwnPtr<WebGLContextProxy> create_webgl_context_proxy(HTML::HTMLCanvasElement&, WebGLVersion, WebGLContextAttributes const&);
|
||||
bool restore_webgl_context_proxy(WebGLContextProxy&, HTML::HTMLCanvasElement&, WebGLVersion, WebGLContextAttributes const&);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,12 +249,19 @@ Optional<WebGLRenderingContextBase::TexImageSourceFrame> WebGLRenderingContextBa
|
|||
};
|
||||
}
|
||||
|
||||
// https://registry.khronos.org/webgl/specs/latest/1.0/#CONTEXT_LOST_WEBGL
|
||||
static constexpr GLenum CONTEXT_LOST_WEBGL = 0x9242;
|
||||
|
||||
// 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()
|
||||
{
|
||||
// A locally-detected failure (currently an upload too large to send over IPC) is reported
|
||||
// before consulting the host.
|
||||
if (m_context_lost) {
|
||||
auto error = m_error;
|
||||
m_error = GL_NO_ERROR;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (auto local_error = context().take_pending_local_error(); local_error != GL_NO_ERROR)
|
||||
return local_error;
|
||||
|
||||
|
|
@ -279,6 +286,52 @@ bool WebGLRenderingContextBase::is_context_lost() const
|
|||
return m_context_lost;
|
||||
}
|
||||
|
||||
void WebGLRenderingContextBase::lose_context_from_compositor_loss()
|
||||
{
|
||||
if (m_context_lost)
|
||||
return;
|
||||
m_context_lost = true;
|
||||
context().set_lost();
|
||||
|
||||
// The next getError() must report CONTEXT_LOST_WEBGL (one-shot) per the spec.
|
||||
m_error = CONTEXT_LOST_WEBGL;
|
||||
|
||||
HTML::queue_a_task(HTML::Task::Source::WebGL, nullptr, nullptr, GC::create_function(heap(), [this, canvas = canvas_for_binding()] {
|
||||
// webglcontextlost is cancelable; preventDefault() means the page wants the context
|
||||
// restored once a compositor is available again.
|
||||
m_context_restore_requested = !fire_webgl_context_event(canvas, EventNames::webglcontextlost);
|
||||
}));
|
||||
}
|
||||
|
||||
void WebGLRenderingContextBase::restore_context_after_compositor_reconnect()
|
||||
{
|
||||
if (!m_context_lost || !m_context_restore_requested)
|
||||
return;
|
||||
|
||||
// A fresh host context starts with no GL objects; per the spec the page re-creates them
|
||||
// in its webglcontextrestored handler.
|
||||
if (!reestablish_remote_context())
|
||||
return;
|
||||
|
||||
reset_context_state_after_loss();
|
||||
m_context_lost = false;
|
||||
m_context_restore_requested = false;
|
||||
m_error = GL_NO_ERROR;
|
||||
|
||||
HTML::queue_a_task(HTML::Task::Source::WebGL, nullptr, nullptr, GC::create_function(heap(), [canvas = canvas_for_binding()] {
|
||||
fire_webgl_context_event(canvas, EventNames::webglcontextrestored);
|
||||
}));
|
||||
}
|
||||
|
||||
void WebGLRenderingContextBase::reset_context_state_after_loss()
|
||||
{
|
||||
++m_context_generation;
|
||||
m_unpack_flip_y = false;
|
||||
m_unpack_premultiply_alpha = false;
|
||||
m_unpack_colorspace_conversion = BROWSER_DEFAULT_WEBGL;
|
||||
reset_client_side_webgl_state();
|
||||
}
|
||||
|
||||
// https://immersive-web.github.io/webxr/#dom-webglrenderingcontextbase-makexrcompatible
|
||||
GC::Ref<WebIDL::Promise> WebGLRenderingContextBase::make_xr_compatible()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -48,8 +48,20 @@ public:
|
|||
virtual WebGLContextProxy& context() = 0;
|
||||
virtual GC::Ref<HTML::HTMLCanvasElement> canvas_for_binding() const = 0;
|
||||
|
||||
u64 context_generation() const { return m_context_generation; }
|
||||
|
||||
bool is_context_lost() const;
|
||||
|
||||
// https://registry.khronos.org/webgl/specs/latest/1.0/#CONTEXT_LOST
|
||||
// The compositor process (and with it the GL state) went away: set the context lost
|
||||
// flag, stop talking to the dead host, and fire webglcontextlost at the canvas.
|
||||
void lose_context_from_compositor_loss();
|
||||
|
||||
// The compositor came back. If the page asked for restoration (called preventDefault on
|
||||
// webglcontextlost), build a fresh remote context and fire webglcontextrestored so the
|
||||
// page can re-create its now-lost GL resources.
|
||||
void restore_context_after_compositor_reconnect();
|
||||
|
||||
bool xr_compatible() const { return m_xr_compatible; }
|
||||
void set_xr_compatible(bool xr_compatible) { m_xr_compatible = xr_compatible; }
|
||||
|
||||
|
|
@ -66,6 +78,12 @@ protected:
|
|||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
// Builds a fresh transport and host context against the reconnected compositor and
|
||||
// rebinds the proxy to it. Returns false if no compositor is available. WebGL1/2
|
||||
// implement it because they own the version and the actual context parameters.
|
||||
virtual bool reestablish_remote_context() = 0;
|
||||
virtual void reset_client_side_webgl_state() = 0;
|
||||
|
||||
// FIXME: Make this and any another instance of extension names a FlyString, similarly to HTML::TagNames
|
||||
bool extension_enabled(StringView extension) const;
|
||||
ReadonlySpan<WebIDL::UnsignedLong> enabled_compressed_texture_formats() const;
|
||||
|
|
@ -153,6 +171,7 @@ protected:
|
|||
|
||||
GLenum get_error_value();
|
||||
void set_error(GLenum error);
|
||||
void reset_context_state_after_loss();
|
||||
|
||||
// UNPACK_FLIP_Y_WEBGL of type boolean
|
||||
// If set, then during any subsequent calls to texImage2D or texSubImage2D, the source data is flipped along
|
||||
|
|
@ -181,9 +200,11 @@ private:
|
|||
// https://registry.khronos.org/webgl/specs/latest/2.0/#webgl-context-lost-flag
|
||||
// Each WebGLRenderingContext and WebGL2RenderingContext has a webgl context lost flag, which is initially unset.
|
||||
bool m_context_lost { false };
|
||||
bool m_context_restore_requested { false };
|
||||
|
||||
// https://immersive-web.github.io/webxr/#xr-compatible
|
||||
bool m_xr_compatible { false };
|
||||
u64 m_context_generation { 0 };
|
||||
|
||||
Vector<WebIDL::UnsignedLong> m_enabled_compressed_texture_formats;
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,30 @@ WebGLRenderingContextImpl::WebGLRenderingContextImpl(JS::Realm& realm, NonnullOw
|
|||
{
|
||||
}
|
||||
|
||||
void WebGLRenderingContextImpl::reset_client_side_webgl_state()
|
||||
{
|
||||
m_array_buffer_binding = nullptr;
|
||||
m_element_array_buffer_binding = nullptr;
|
||||
m_current_program = nullptr;
|
||||
m_framebuffer_binding = nullptr;
|
||||
m_renderbuffer_binding = nullptr;
|
||||
m_texture_binding_2d = nullptr;
|
||||
m_texture_binding_cube_map = nullptr;
|
||||
m_uniform_buffer_binding = nullptr;
|
||||
m_copy_read_buffer_binding = nullptr;
|
||||
m_copy_write_buffer_binding = nullptr;
|
||||
m_transform_feedback_buffer_binding = nullptr;
|
||||
m_pixel_pack_buffer_binding = nullptr;
|
||||
m_pixel_unpack_buffer_binding = nullptr;
|
||||
m_texture_binding_2d_array = nullptr;
|
||||
m_texture_binding_3d = nullptr;
|
||||
m_transform_feedback_binding = nullptr;
|
||||
m_current_vertex_array = nullptr;
|
||||
m_any_samples_passed = nullptr;
|
||||
m_any_samples_passed_conservative = nullptr;
|
||||
m_transform_feedback_primitives_written = nullptr;
|
||||
}
|
||||
|
||||
void WebGLRenderingContextImpl::active_texture(WebIDL::UnsignedLong texture)
|
||||
{
|
||||
m_context->make_current();
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ public:
|
|||
|
||||
protected:
|
||||
virtual void visit_edges(JS::Cell::Visitor&) override;
|
||||
virtual void reset_client_side_webgl_state() override;
|
||||
|
||||
GC::Ptr<WebGLBuffer> m_array_buffer_binding;
|
||||
GC::Ptr<WebGLBuffer> m_element_array_buffer_binding;
|
||||
|
|
|
|||
|
|
@ -308,6 +308,9 @@ void CompositorConnection::did_lose_compositor()
|
|||
entry.value.callback();
|
||||
}
|
||||
m_screenshots.clear();
|
||||
|
||||
if (on_compositor_lost)
|
||||
on_compositor_lost();
|
||||
}
|
||||
|
||||
bool CompositorConnection::can_send_message_to_compositor() const
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ public:
|
|||
void read_webgl_buffer_sub_data(Web::Painting::CanvasId, Web::WebGL::GLenum target, Web::WebGL::GLintptr offset, Web::WebGL::GLintptr size, Core::AnonymousBuffer const& data);
|
||||
|
||||
Function<void(u64 page_id, Web::MouseEvent)> on_mouse_event;
|
||||
Function<void()> on_compositor_lost;
|
||||
|
||||
private:
|
||||
struct PendingScreenshot {
|
||||
|
|
|
|||
|
|
@ -201,6 +201,9 @@ void ConnectionFromClient::connect_to_compositor_process(IPC::TransportHandle ha
|
|||
m_compositor_connection->on_mouse_event = [this](u64 page_id, Web::MouseEvent event) {
|
||||
mouse_event(page_id, move(event));
|
||||
};
|
||||
m_compositor_connection->on_compositor_lost = [this] {
|
||||
m_page_host->compositor_process_lost();
|
||||
};
|
||||
|
||||
#ifdef AK_OS_WINDOWS
|
||||
// Perform Windows peer PID handshake before any other IPC
|
||||
|
|
|
|||
|
|
@ -250,9 +250,15 @@ void PageClient::set_window_size(Web::DevicePixelSize size)
|
|||
page().set_window_size(size);
|
||||
}
|
||||
|
||||
void PageClient::compositor_process_lost()
|
||||
{
|
||||
page().notify_all_webgl_contexts_lost();
|
||||
}
|
||||
|
||||
void PageClient::compositor_process_reconnected()
|
||||
{
|
||||
page().top_level_traversable()->repaint_after_compositor_process_reconnect();
|
||||
page().notify_all_canvas_elements_of_lost_backing_storage();
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ public:
|
|||
void set_window_position(Web::DevicePixelPoint);
|
||||
void set_window_size(Web::DevicePixelSize);
|
||||
void compositor_process_reconnected();
|
||||
void compositor_process_lost();
|
||||
|
||||
void toggle_media_play_state();
|
||||
void toggle_media_mute_state();
|
||||
|
|
|
|||
|
|
@ -64,4 +64,10 @@ void PageHost::compositor_process_reconnected()
|
|||
page->compositor_process_reconnected();
|
||||
}
|
||||
|
||||
void PageHost::compositor_process_lost()
|
||||
{
|
||||
for (auto& [_, page] : m_pages)
|
||||
page->compositor_process_lost();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public:
|
|||
ConnectionFromClient& client() const { return m_client; }
|
||||
void ensure_compositor_host();
|
||||
void compositor_process_reconnected();
|
||||
void compositor_process_lost();
|
||||
Web::Compositor::CompositorHost* compositor_host() { return m_compositor_host.ptr(); }
|
||||
Web::Compositor::CompositorHost const* compositor_host() const { return m_compositor_host.ptr(); }
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue