2022-06-04 00:22:42 -03:00
/*
* Copyright ( c ) 2022 , Luke Wilde < lukew @ serenityos . org >
2024-11-29 16:49:59 -03:00
* Copyright ( c ) 2023 , Aliaksandr Kalenik < kalenik . aliaksandr @ gmail . com >
2022-06-04 00:22:42 -03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2024-11-29 16:49:59 -03:00
# include <LibJS/Runtime/ArrayBuffer.h>
# include <LibJS/Runtime/TypedArray.h>
2022-09-25 21:12:50 -03:00
# include <LibWeb/Bindings/Intrinsics.h>
2026-05-01 14:01:23 -03:00
# include <LibWeb/Bindings/WebGLContextEvent.h>
2026-04-18 05:54:06 -03:00
# include <LibWeb/Bindings/WebGLRenderingContext.h>
2026-06-15 14:37:51 -03:00
# include <LibWeb/Compositor/CompositorHost.h>
# include <LibWeb/DOM/Document.h>
2022-06-04 00:22:42 -03:00
# include <LibWeb/HTML/HTMLCanvasElement.h>
2026-06-15 14:37:51 -03:00
# include <LibWeb/HTML/Navigable.h>
2025-01-10 10:39:53 -03:00
# include <LibWeb/Infra/Strings.h>
2026-06-15 14:37:51 -03:00
# include <LibWeb/Page/Page.h>
2024-11-29 16:49:59 -03:00
# include <LibWeb/Painting/Paintable.h>
2023-04-09 07:57:23 -03:00
# include <LibWeb/WebGL/EventNames.h>
2026-06-15 14:37:51 -03:00
# include <LibWeb/WebGL/RemoteWebGLTransport.h>
2022-06-04 00:22:42 -03:00
# include <LibWeb/WebGL/WebGLContextEvent.h>
2026-06-15 14:37:51 -03:00
# include <LibWeb/WebGL/WebGLContextProxy.h>
2022-06-04 00:22:42 -03:00
# include <LibWeb/WebGL/WebGLRenderingContext.h>
2024-11-29 16:49:59 -03:00
# include <LibWeb/WebGL/WebGLShader.h>
# include <LibWeb/WebIDL/Buffers.h>
# include <GLES2/gl2.h>
# include <GLES2/gl2ext.h>
2022-06-04 00:22:42 -03:00
namespace Web : : WebGL {
2024-11-14 12:01:23 -03:00
GC_DEFINE_ALLOCATOR ( WebGLRenderingContext ) ;
2023-11-19 15:47:52 -03:00
2026-06-15 13:53:42 -03:00
// 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.
2026-06-15 14:37:51 -03:00
bool fire_webgl_context_event ( HTML : : HTMLCanvasElement & canvas_element , FlyString const & type )
2022-06-04 00:22:42 -03:00
{
// 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.
2026-05-01 14:01:23 -03:00
auto event = WebGLContextEvent : : create ( canvas_element . realm ( ) , type , Bindings : : WebGLContextEventInit { } ) ;
2022-06-04 00:22:42 -03:00
event - > set_is_trusted ( true ) ;
event - > set_cancelable ( true ) ;
2026-06-15 14:37:51 -03:00
return canvas_element . dispatch_event ( * event ) ;
2022-06-04 00:22:42 -03:00
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#fire-a-webgl-context-creation-error
2024-12-06 00:56:18 -03:00
void fire_webgl_context_creation_error ( HTML : : HTMLCanvasElement & canvas_element )
2022-06-04 00:22:42 -03:00
{
// 1. Fire a WebGL context event named "webglcontextcreationerror" at canvas, optionally with its statusMessage attribute set to a platform dependent string about the nature of the failure.
2023-04-09 07:57:23 -03:00
fire_webgl_context_event ( canvas_element , EventNames : : webglcontextcreationerror ) ;
2022-06-04 00:22:42 -03:00
}
2026-06-15 14:37:51 -03:00
// 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 ) ,
} ;
}
2026-06-15 13:53:42 -03:00
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 )
2026-06-15 14:37:51 -03:00
{
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 { } ;
2026-06-15 13:53:42 -03:00
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 ;
2026-06-15 14:37:51 -03:00
}
2024-11-14 12:01:23 -03:00
JS : : ThrowCompletionOr < GC : : Ptr < WebGLRenderingContext > > WebGLRenderingContext : : create ( JS : : Realm & realm , HTML : : HTMLCanvasElement & canvas_element , JS : : Value options )
2022-06-04 00:22:42 -03:00
{
// We should be coming here from getContext being called on a wrapped <canvas> element.
2022-08-28 08:42:07 -03:00
auto context_attributes = TRY ( convert_value_to_context_attributes_dictionary ( canvas_element . vm ( ) , options ) ) ;
2022-06-04 00:22:42 -03:00
2026-06-15 14:37:51 -03:00
auto context = create_webgl_context_proxy ( canvas_element , WebGLVersion : : WebGL1 , context_attributes ) ;
2024-01-18 16:29:09 -03:00
if ( ! context ) {
2022-09-16 08:58:30 -03:00
fire_webgl_context_creation_error ( canvas_element ) ;
2024-11-14 12:01:23 -03:00
return GC : : Ptr < WebGLRenderingContext > { nullptr } ;
2022-09-16 08:58:30 -03:00
}
2024-01-18 16:29:09 -03:00
2024-11-13 13:50:17 -03:00
return realm . create < WebGLRenderingContext > ( realm , canvas_element , context . release_nonnull ( ) , context_attributes , context_attributes ) ;
2022-06-04 00:22:42 -03:00
}
2026-06-15 14:37:51 -03:00
WebGLRenderingContext : : WebGLRenderingContext ( JS : : Realm & realm , HTML : : HTMLCanvasElement & canvas_element , NonnullOwnPtr < WebGLContextProxy > context , WebGLContextAttributes context_creation_parameters , WebGLContextAttributes actual_context_parameters )
2025-12-01 18:01:17 -03:00
: WebGLRenderingContextOverloads ( realm , move ( context ) )
2024-11-29 16:49:59 -03:00
, m_canvas_element ( canvas_element )
, m_context_creation_parameters ( context_creation_parameters )
, m_actual_context_parameters ( actual_context_parameters )
2022-09-02 10:53:02 -03:00
{
}
WebGLRenderingContext : : ~ WebGLRenderingContext ( ) = default ;
2023-08-07 03:41:28 -03:00
void WebGLRenderingContext : : initialize ( JS : : Realm & realm )
2023-01-10 08:28:20 -03:00
{
2024-03-16 09:13:08 -03:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( WebGLRenderingContext ) ;
2025-04-20 11:22:57 -03:00
Base : : initialize ( realm ) ;
2023-01-10 08:28:20 -03:00
}
2024-11-29 16:49:59 -03:00
void WebGLRenderingContext : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2024-12-24 15:16:29 -03:00
WebGLRenderingContextImpl : : visit_edges ( visitor ) ;
2024-11-29 16:49:59 -03:00
visitor . visit ( m_canvas_element ) ;
}
2026-06-15 14:37:51 -03:00
void WebGLRenderingContext : : prepare_for_compositing ( )
2024-11-29 16:49:59 -03:00
{
2026-06-15 14:37:51 -03:00
context ( ) . present_canvas_for_compositing ( m_context_creation_parameters . preserve_drawing_buffer ) ;
2024-11-29 16:49:59 -03:00
}
2026-06-15 13:53:42 -03:00
bool WebGLRenderingContext : : reestablish_remote_context ( )
{
return restore_webgl_context_proxy ( context ( ) , * m_canvas_element , WebGLVersion : : WebGL1 , m_actual_context_parameters ) ;
}
2024-11-29 16:49:59 -03:00
GC : : Ref < HTML : : HTMLCanvasElement > WebGLRenderingContext : : canvas_for_binding ( ) const
{
return * m_canvas_element ;
}
2026-06-15 14:37:51 -03:00
void WebGLRenderingContext : : did_update_canvas_content ( )
2024-11-29 16:49:59 -03:00
{
2026-02-19 15:31:20 -03:00
m_canvas_element - > set_canvas_content_dirty ( ) ;
2024-11-29 16:49:59 -03:00
2026-03-04 12:02:14 -03:00
m_canvas_element - > set_needs_repaint ( ) ;
2024-11-29 16:49:59 -03:00
}
2024-12-04 22:59:00 -03:00
Optional < WebGLContextAttributes > WebGLRenderingContext : : get_context_attributes ( )
{
if ( is_context_lost ( ) )
return { } ;
return m_actual_context_parameters ;
}
2024-11-29 18:15:02 -03:00
void WebGLRenderingContext : : set_size ( Gfx : : IntSize const & size )
2024-11-29 16:49:59 -03:00
{
2024-12-24 15:22:44 -03:00
Gfx : : IntSize final_size ;
2026-06-15 14:37:51 -03:00
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 ) ) ;
2024-12-24 15:22:44 -03:00
context ( ) . set_size ( final_size ) ;
2024-11-29 16:49:59 -03:00
}
void WebGLRenderingContext : : reset_to_default_state ( )
{
}
2024-12-10 00:52:05 -03:00
WebIDL : : Long WebGLRenderingContext : : drawing_buffer_width ( ) const
{
auto size = canvas_for_binding ( ) - > bitmap_size_for_canvas ( ) ;
2026-06-15 14:37:51 -03:00
return min ( size . width ( ) , max_webgl_drawing_buffer_dimension ) ;
2024-12-10 00:52:05 -03:00
}
WebIDL : : Long WebGLRenderingContext : : drawing_buffer_height ( ) const
{
auto size = canvas_for_binding ( ) - > bitmap_size_for_canvas ( ) ;
2026-06-15 14:37:51 -03:00
return min ( size . height ( ) , max_webgl_drawing_buffer_dimension ) ;
2024-12-10 00:52:05 -03:00
}
2022-06-04 00:22:42 -03:00
}