2022-06-04 04:22:42 +01:00
/*
* Copyright ( c ) 2022 , Luke Wilde < lukew @ serenityos . org >
2024-11-29 20:49:59 +01:00
* Copyright ( c ) 2023 , Aliaksandr Kalenik < kalenik . aliaksandr @ gmail . com >
2022-06-04 04:22:42 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2024-11-29 20:49:59 +01:00
# include <LibJS/Runtime/ArrayBuffer.h>
# include <LibJS/Runtime/TypedArray.h>
2022-09-25 18:12:50 -06:00
# include <LibWeb/Bindings/Intrinsics.h>
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/WebGLRenderingContextPrototype.h>
2022-06-04 04:22:42 +01:00
# include <LibWeb/HTML/HTMLCanvasElement.h>
2024-11-29 20:49:59 +01:00
# include <LibWeb/HTML/TraversableNavigable.h>
2025-01-10 13:39:53 +00:00
# include <LibWeb/Infra/Strings.h>
2024-11-29 20:49:59 +01:00
# include <LibWeb/Painting/Paintable.h>
2023-04-09 12:57:23 +02:00
# include <LibWeb/WebGL/EventNames.h>
2025-01-10 14:27:03 +00:00
# include <LibWeb/WebGL/Extensions/ANGLEInstancedArrays.h>
2025-04-07 16:47:26 +01:00
# include <LibWeb/WebGL/Extensions/EXTBlendMinMax.h>
2025-01-10 14:27:03 +00:00
# include <LibWeb/WebGL/Extensions/OESVertexArrayObject.h>
2025-01-28 18:25:26 +00:00
# include <LibWeb/WebGL/Extensions/WebGLCompressedTextureS3tc.h>
2025-01-10 16:36:00 +00:00
# include <LibWeb/WebGL/Extensions/WebGLDrawBuffers.h>
2024-11-29 22:34:46 +01:00
# include <LibWeb/WebGL/OpenGLContext.h>
2022-06-04 04:22:42 +01:00
# include <LibWeb/WebGL/WebGLContextEvent.h>
# include <LibWeb/WebGL/WebGLRenderingContext.h>
2024-11-29 20:49:59 +01:00
# include <LibWeb/WebGL/WebGLShader.h>
# include <LibWeb/WebIDL/Buffers.h>
# include <GLES2/gl2.h>
# include <GLES2/gl2ext.h>
2022-06-04 04:22:42 +01:00
namespace Web : : WebGL {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( WebGLRenderingContext ) ;
2023-11-19 19:47:52 +01:00
2022-06-04 04:22:42 +01:00
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#fire-a-webgl-context-event
2024-12-05 20:56:18 -07:00
void fire_webgl_context_event ( HTML : : HTMLCanvasElement & canvas_element , FlyString const & type )
2022-06-04 04:22:42 +01: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.
2023-08-13 13:05:26 +02:00
auto event = WebGLContextEvent : : create ( canvas_element . realm ( ) , type , WebGLContextEventInit { } ) ;
2022-06-04 04:22:42 +01:00
event - > set_is_trusted ( true ) ;
event - > set_cancelable ( true ) ;
2022-08-08 22:29:40 +02:00
canvas_element . dispatch_event ( * event ) ;
2022-06-04 04:22:42 +01:00
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#fire-a-webgl-context-creation-error
2024-12-05 20:56:18 -07:00
void fire_webgl_context_creation_error ( HTML : : HTMLCanvasElement & canvas_element )
2022-06-04 04:22:42 +01: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 12:57:23 +02:00
fire_webgl_context_event ( canvas_element , EventNames : : webglcontextcreationerror ) ;
2022-06-04 04:22:42 +01:00
}
2024-11-15 04:01:23 +13:00
JS : : ThrowCompletionOr < GC : : Ptr < WebGLRenderingContext > > WebGLRenderingContext : : create ( JS : : Realm & realm , HTML : : HTMLCanvasElement & canvas_element , JS : : Value options )
2022-06-04 04:22:42 +01:00
{
// We should be coming here from getContext being called on a wrapped <canvas> element.
2022-08-28 13:42:07 +02:00
auto context_attributes = TRY ( convert_value_to_context_attributes_dictionary ( canvas_element . vm ( ) , options ) ) ;
2022-06-04 04:22:42 +01:00
2024-11-29 22:15:02 +01:00
auto skia_backend_context = canvas_element . navigable ( ) - > traversable_navigable ( ) - > skia_backend_context ( ) ;
2024-12-05 22:27:00 -07:00
if ( ! skia_backend_context ) {
fire_webgl_context_creation_error ( canvas_element ) ;
return GC : : Ptr < WebGLRenderingContext > { nullptr } ;
}
2025-03-23 10:38:00 +00:00
auto context = OpenGLContext : : create ( * skia_backend_context , OpenGLContext : : WebGLVersion : : WebGL1 ) ;
2024-01-18 20:29:09 +01:00
if ( ! context ) {
2022-09-16 05:58:30 -06:00
fire_webgl_context_creation_error ( canvas_element ) ;
2024-11-15 04:01:23 +13:00
return GC : : Ptr < WebGLRenderingContext > { nullptr } ;
2022-09-16 05:58:30 -06:00
}
2024-01-18 20:29:09 +01:00
2024-11-29 22:15:02 +01:00
context - > set_size ( canvas_element . bitmap_size_for_canvas ( 1 , 1 ) ) ;
2024-11-14 05:50:17 +13:00
return realm . create < WebGLRenderingContext > ( realm , canvas_element , context . release_nonnull ( ) , context_attributes , context_attributes ) ;
2022-06-04 04:22:42 +01:00
}
2024-01-18 20:29:09 +01:00
WebGLRenderingContext : : WebGLRenderingContext ( JS : : Realm & realm , HTML : : HTMLCanvasElement & canvas_element , NonnullOwnPtr < OpenGLContext > context , WebGLContextAttributes context_creation_parameters , WebGLContextAttributes actual_context_parameters )
2024-11-29 20:49:59 +01:00
: PlatformObject ( realm )
2024-11-29 22:34:46 +01:00
, WebGLRenderingContextImpl ( realm , move ( context ) )
2024-11-29 20:49:59 +01:00
, m_canvas_element ( canvas_element )
, m_context_creation_parameters ( context_creation_parameters )
, m_actual_context_parameters ( actual_context_parameters )
2022-09-02 15:53:02 +02:00
{
}
WebGLRenderingContext : : ~ WebGLRenderingContext ( ) = default ;
2023-08-07 08:41:28 +02:00
void WebGLRenderingContext : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( WebGLRenderingContext ) ;
2025-04-20 16:22:57 +02:00
Base : : initialize ( realm ) ;
2023-01-10 06:28:20 -05:00
}
2024-11-29 20:49:59 +01:00
void WebGLRenderingContext : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2024-12-24 18:16:29 +00:00
WebGLRenderingContextImpl : : visit_edges ( visitor ) ;
2024-11-29 20:49:59 +01:00
visitor . visit ( m_canvas_element ) ;
2025-01-10 13:39:53 +00:00
visitor . visit ( m_angle_instanced_arrays_extension ) ;
2025-04-07 16:47:26 +01:00
visitor . visit ( m_ext_blend_min_max_extension ) ;
2025-01-10 13:39:53 +00:00
visitor . visit ( m_oes_vertex_array_object_extension ) ;
2025-01-28 18:25:26 +00:00
visitor . visit ( m_webgl_compressed_texture_s3tc_extension ) ;
2025-01-10 16:36:00 +00:00
visitor . visit ( m_webgl_draw_buffers_extension ) ;
2024-11-29 20:49:59 +01:00
}
void WebGLRenderingContext : : present ( )
{
if ( ! m_should_present )
return ;
m_should_present = false ;
2025-03-23 11:27:39 +00:00
context ( ) . present ( m_context_creation_parameters . preserve_drawing_buffer ) ;
2024-11-29 20:49:59 +01:00
}
GC : : Ref < HTML : : HTMLCanvasElement > WebGLRenderingContext : : canvas_for_binding ( ) const
{
return * m_canvas_element ;
}
void WebGLRenderingContext : : needs_to_present ( )
{
m_should_present = true ;
if ( ! m_canvas_element - > paintable ( ) )
return ;
m_canvas_element - > paintable ( ) - > set_needs_display ( ) ;
}
void WebGLRenderingContext : : set_error ( GLenum error )
{
auto context_error = glGetError ( ) ;
if ( context_error ! = GL_NO_ERROR )
m_error = context_error ;
else
m_error = error ;
}
bool WebGLRenderingContext : : is_context_lost ( ) const
{
dbgln_if ( WEBGL_CONTEXT_DEBUG , " WebGLRenderingContext::is_context_lost() " ) ;
return m_context_lost ;
}
2024-12-05 02:59:00 +01:00
Optional < WebGLContextAttributes > WebGLRenderingContext : : get_context_attributes ( )
{
if ( is_context_lost ( ) )
return { } ;
return m_actual_context_parameters ;
}
2024-11-29 22:15:02 +01:00
void WebGLRenderingContext : : set_size ( Gfx : : IntSize const & size )
2024-11-29 20:49:59 +01:00
{
2024-12-24 18:22:44 +00:00
Gfx : : IntSize final_size ;
final_size . set_width ( max ( size . width ( ) , 1 ) ) ;
final_size . set_height ( max ( size . height ( ) , 1 ) ) ;
context ( ) . set_size ( final_size ) ;
2024-11-29 20:49:59 +01:00
}
void WebGLRenderingContext : : reset_to_default_state ( )
{
}
RefPtr < Gfx : : PaintingSurface > WebGLRenderingContext : : surface ( )
{
2024-11-29 22:34:46 +01:00
return context ( ) . surface ( ) ;
2024-11-29 22:15:02 +01:00
}
void WebGLRenderingContext : : allocate_painting_surface_if_needed ( )
{
2024-11-29 22:34:46 +01:00
context ( ) . allocate_painting_surface_if_needed ( ) ;
2024-11-29 20:49:59 +01:00
}
2024-12-10 05:49:33 +01:00
Optional < Vector < String > > WebGLRenderingContext : : get_supported_extensions ( )
2024-12-02 21:25:20 +01:00
{
2024-12-10 05:49:33 +01:00
return context ( ) . get_supported_extensions ( ) ;
2024-12-02 21:25:20 +01:00
}
2024-12-20 11:26:22 +01:00
JS : : Object * WebGLRenderingContext : : get_extension ( String const & name )
2024-12-02 21:25:20 +01:00
{
2025-01-10 13:39:53 +00:00
// Returns an object if, and only if, name is an ASCII case-insensitive match [HTML] for one of the names returned
// from getSupportedExtensions; otherwise, returns null. The object returned from getExtension contains any constants
// or functions provided by the extension. A returned object may have no constants or functions if the extension does
// not define any, but a unique object must still be returned. That object is used to indicate that the extension has
// been enabled.
auto supported_extensions = get_supported_extensions ( ) ;
auto supported_extension_iterator = supported_extensions - > find_if ( [ & name ] ( String const & supported_extension ) {
2025-05-18 15:04:56 +12:00
return supported_extension . equals_ignoring_ascii_case ( name ) ;
2025-01-10 13:39:53 +00:00
} ) ;
if ( supported_extension_iterator = = supported_extensions - > end ( ) )
return nullptr ;
2025-05-18 15:04:56 +12:00
if ( name . equals_ignoring_ascii_case ( " ANGLE_instanced_arrays " sv ) ) {
2025-01-10 13:39:53 +00:00
if ( ! m_angle_instanced_arrays_extension ) {
2025-01-10 14:27:03 +00:00
m_angle_instanced_arrays_extension = MUST ( Extensions : : ANGLEInstancedArrays : : create ( realm ( ) , * this ) ) ;
2025-01-10 13:39:53 +00:00
}
VERIFY ( m_angle_instanced_arrays_extension ) ;
return m_angle_instanced_arrays_extension ;
2024-12-20 11:26:22 +01:00
}
2025-01-10 13:39:53 +00:00
2025-05-18 15:04:56 +12:00
if ( name . equals_ignoring_ascii_case ( " EXT_blend_minmax " sv ) ) {
2025-04-07 16:47:26 +01:00
if ( ! m_ext_blend_min_max_extension ) {
m_ext_blend_min_max_extension = MUST ( Extensions : : EXTBlendMinMax : : create ( realm ( ) , * this ) ) ;
}
VERIFY ( m_ext_blend_min_max_extension ) ;
return m_ext_blend_min_max_extension ;
}
2025-05-18 15:04:56 +12:00
if ( name . equals_ignoring_ascii_case ( " OES_vertex_array_object " sv ) ) {
2025-01-10 13:39:53 +00:00
if ( ! m_oes_vertex_array_object_extension ) {
2025-01-10 14:27:03 +00:00
m_oes_vertex_array_object_extension = MUST ( Extensions : : OESVertexArrayObject : : create ( realm ( ) , * this ) ) ;
2025-01-10 13:39:53 +00:00
}
VERIFY ( m_oes_vertex_array_object_extension ) ;
return m_oes_vertex_array_object_extension ;
2025-01-09 19:17:19 +00:00
}
2025-01-10 13:39:53 +00:00
2025-05-18 15:04:56 +12:00
if ( name . equals_ignoring_ascii_case ( " WEBGL_compressed_texture_s3tc " sv ) ) {
2025-01-28 18:25:26 +00:00
if ( ! m_webgl_compressed_texture_s3tc_extension ) {
m_webgl_compressed_texture_s3tc_extension = MUST ( Extensions : : WebGLCompressedTextureS3tc : : create ( realm ( ) , this ) ) ;
}
VERIFY ( m_webgl_compressed_texture_s3tc_extension ) ;
return m_webgl_compressed_texture_s3tc_extension ;
}
2025-05-18 15:04:56 +12:00
if ( name . equals_ignoring_ascii_case ( " WEBGL_draw_buffers " sv ) ) {
2025-01-10 16:36:00 +00:00
if ( ! m_webgl_draw_buffers_extension ) {
m_webgl_draw_buffers_extension = MUST ( Extensions : : WebGLDrawBuffers : : create ( realm ( ) , * this ) ) ;
}
VERIFY ( m_webgl_draw_buffers_extension ) ;
return m_webgl_draw_buffers_extension ;
}
2024-12-02 21:25:20 +01:00
return nullptr ;
}
2024-12-10 04:52:05 +01:00
WebIDL : : Long WebGLRenderingContext : : drawing_buffer_width ( ) const
{
auto size = canvas_for_binding ( ) - > bitmap_size_for_canvas ( ) ;
return size . width ( ) ;
}
WebIDL : : Long WebGLRenderingContext : : drawing_buffer_height ( ) const
{
auto size = canvas_for_binding ( ) - > bitmap_size_for_canvas ( ) ;
return size . height ( ) ;
}
2022-06-04 04:22:42 +01:00
}