Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2022, Luke Wilde <lukew@serenityos.org>
3
 * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#include <AK/Debug.h>
9
#include <LibWeb/HTML/HTMLCanvasElement.h>
10
#include <LibWeb/Layout/Node.h>
11
#include <LibWeb/Painting/Paintable.h>
12
#include <LibWeb/WebGL/WebGLRenderingContextBase.h>
13
14
namespace Web::WebGL {
15
16
// FIXME: Replace with constants defined in WebGL spec.
17
#define GL_INVALID_OPERATION 0x0502
18
#define GL_INVALID_VALUE 0x0501
19
0
#define GL_FRONT_AND_BACK 0x0408
20
21
WebGLRenderingContextBase::WebGLRenderingContextBase(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, NonnullOwnPtr<OpenGLContext> context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters)
22
0
    : PlatformObject(realm)
23
0
    , m_canvas_element(canvas_element)
24
0
    , m_context(move(context))
25
0
    , m_context_creation_parameters(move(context_creation_parameters))
26
0
    , m_actual_context_parameters(move(actual_context_parameters))
27
0
{
28
0
}
29
30
0
WebGLRenderingContextBase::~WebGLRenderingContextBase() = default;
31
32
void WebGLRenderingContextBase::visit_edges(Cell::Visitor& visitor)
33
0
{
34
0
    Base::visit_edges(visitor);
35
0
    visitor.visit(m_canvas_element);
36
0
}
37
38
#define RETURN_WITH_WEBGL_ERROR_IF(condition, error)                         \
39
0
    if (condition) {                                                         \
40
0
        dbgln_if(WEBGL_CONTEXT_DEBUG, "{}(): error {:#x}", __func__, error); \
41
0
        set_error(error);                                                    \
42
0
        return;                                                              \
43
0
    }
44
45
void WebGLRenderingContextBase::present()
46
0
{
47
0
    if (!m_should_present)
48
0
        return;
49
50
0
    m_should_present = false;
51
52
    // "Before the drawing buffer is presented for compositing the implementation shall ensure that all rendering operations have been flushed to the drawing buffer."
53
    // FIXME: Is this the operation it means?
54
0
    m_context->gl_flush();
55
56
0
    m_context->present(*canvas_element().bitmap());
57
58
    // "By default, after compositing the contents of the drawing buffer shall be cleared to their default values, as shown in the table above.
59
    // This default behavior can be changed by setting the preserveDrawingBuffer attribute of the WebGLContextAttributes object.
60
    // If this flag is true, the contents of the drawing buffer shall be preserved until the author either clears or overwrites them."
61
0
    if (!m_context_creation_parameters.preserve_drawing_buffer) {
62
0
        m_context->clear_buffer_to_default_values();
63
0
    }
64
0
}
65
66
HTML::HTMLCanvasElement& WebGLRenderingContextBase::canvas_element()
67
0
{
68
0
    return *m_canvas_element;
69
0
}
70
71
HTML::HTMLCanvasElement const& WebGLRenderingContextBase::canvas_element() const
72
0
{
73
0
    return *m_canvas_element;
74
0
}
75
76
JS::NonnullGCPtr<HTML::HTMLCanvasElement> WebGLRenderingContextBase::canvas_for_binding() const
77
0
{
78
0
    return *m_canvas_element;
79
0
}
80
81
void WebGLRenderingContextBase::needs_to_present()
82
0
{
83
0
    m_should_present = true;
84
85
0
    if (!canvas_element().paintable())
86
0
        return;
87
0
    canvas_element().paintable()->set_needs_display();
88
0
}
89
90
void WebGLRenderingContextBase::set_error(GLenum error)
91
0
{
92
0
    auto context_error = m_context->gl_get_error();
93
0
    if (context_error != GL_NO_ERROR)
94
0
        m_error = context_error;
95
0
    else
96
0
        m_error = error;
97
0
}
98
99
bool WebGLRenderingContextBase::is_context_lost() const
100
0
{
101
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::is_context_lost()");
102
0
    return m_context_lost;
103
0
}
104
105
Optional<Vector<String>> WebGLRenderingContextBase::get_supported_extensions() const
106
0
{
107
0
    if (m_context_lost)
108
0
        return Optional<Vector<String>> {};
109
110
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::get_supported_extensions()");
111
112
    // FIXME: We don't currently support any extensions.
113
0
    return Vector<String> {};
114
0
}
115
116
JS::Object* WebGLRenderingContextBase::get_extension(String const& name) const
117
0
{
118
0
    if (m_context_lost)
119
0
        return nullptr;
120
121
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::get_extension(name='{}')", name);
122
123
    // FIXME: We don't currently support any extensions.
124
0
    return nullptr;
125
0
}
126
127
void WebGLRenderingContextBase::active_texture(GLenum texture)
128
0
{
129
0
    if (m_context_lost)
130
0
        return;
131
132
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::active_texture(texture={:#08x})", texture);
133
0
    m_context->gl_active_texture(texture);
134
0
}
135
136
void WebGLRenderingContextBase::clear(GLbitfield mask)
137
0
{
138
0
    if (m_context_lost)
139
0
        return;
140
141
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::clear(mask={:#08x})", mask);
142
0
    m_context->gl_clear(mask);
143
144
    // FIXME: This should only be done if this is targeting the front buffer.
145
0
    needs_to_present();
146
0
}
147
148
void WebGLRenderingContextBase::clear_color(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
149
0
{
150
0
    if (m_context_lost)
151
0
        return;
152
153
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::clear_color(red={}, green={}, blue={}, alpha={})", red, green, blue, alpha);
154
0
    m_context->gl_clear_color(red, green, blue, alpha);
155
0
}
156
157
void WebGLRenderingContextBase::clear_depth(GLclampf depth)
158
0
{
159
0
    if (m_context_lost)
160
0
        return;
161
162
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::clear_depth(depth={})", depth);
163
0
    m_context->gl_clear_depth(depth);
164
0
}
165
166
void WebGLRenderingContextBase::clear_stencil(GLint s)
167
0
{
168
0
    if (m_context_lost)
169
0
        return;
170
171
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::clear_stencil(s={:#08x})", s);
172
0
    m_context->gl_clear_stencil(s);
173
0
}
174
175
void WebGLRenderingContextBase::color_mask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)
176
0
{
177
0
    if (m_context_lost)
178
0
        return;
179
180
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::color_mask(red={}, green={}, blue={}, alpha={})", red, green, blue, alpha);
181
0
    m_context->gl_color_mask(red, green, blue, alpha);
182
0
}
183
184
void WebGLRenderingContextBase::cull_face(GLenum mode)
185
0
{
186
0
    if (m_context_lost)
187
0
        return;
188
189
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::cull_face(mode={:#08x})", mode);
190
0
    m_context->gl_cull_face(mode);
191
0
}
192
193
void WebGLRenderingContextBase::depth_func(GLenum func)
194
0
{
195
0
    if (m_context_lost)
196
0
        return;
197
198
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::depth_func(func={:#08x})", func);
199
0
    m_context->gl_depth_func(func);
200
0
}
201
202
void WebGLRenderingContextBase::depth_mask(GLboolean mask)
203
0
{
204
0
    if (m_context_lost)
205
0
        return;
206
207
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::depth_mask(mask={})", mask);
208
0
    m_context->gl_depth_mask(mask);
209
0
}
210
211
void WebGLRenderingContextBase::depth_range(GLclampf z_near, GLclampf z_far)
212
0
{
213
0
    if (m_context_lost)
214
0
        return;
215
216
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::depth_range(z_near={}, z_far={})", z_near, z_far);
217
218
    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#VIEWPORT_DEPTH_RANGE
219
    // "The WebGL API does not support depth ranges with where the near plane is mapped to a value greater than that of the far plane. A call to depthRange will generate an INVALID_OPERATION error if zNear is greater than zFar."
220
0
    RETURN_WITH_WEBGL_ERROR_IF(z_near > z_far, GL_INVALID_OPERATION);
221
0
    m_context->gl_depth_range(z_near, z_far);
222
0
}
223
224
void WebGLRenderingContextBase::finish()
225
0
{
226
0
    if (m_context_lost)
227
0
        return;
228
229
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::finish()");
230
0
    m_context->gl_finish();
231
0
}
232
233
void WebGLRenderingContextBase::flush()
234
0
{
235
0
    if (m_context_lost)
236
0
        return;
237
238
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::flush()");
239
0
    m_context->gl_flush();
240
0
}
241
242
void WebGLRenderingContextBase::front_face(GLenum mode)
243
0
{
244
0
    if (m_context_lost)
245
0
        return;
246
247
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::front_face(mode={:#08x})", mode);
248
0
    m_context->gl_front_face(mode);
249
0
}
250
251
GLenum WebGLRenderingContextBase::get_error()
252
0
{
253
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::get_error()");
254
255
    // "If the context's webgl context lost flag is set, returns CONTEXT_LOST_WEBGL the first time this method is called. Afterward, returns NO_ERROR until the context has been restored."
256
    // FIXME: The plan here is to make the context lost handler unconditionally set m_error to CONTEXT_LOST_WEBGL, which we currently do not have.
257
    //        The idea for the unconditional set is that any potentially error generating functions will not execute when the context is lost.
258
0
    if (m_error != GL_NO_ERROR || m_context_lost) {
259
0
        auto last_error = m_error;
260
0
        m_error = GL_NO_ERROR;
261
0
        return last_error;
262
0
    }
263
264
0
    return m_context->gl_get_error();
265
0
}
266
267
void WebGLRenderingContextBase::line_width(GLfloat width)
268
0
{
269
0
    if (m_context_lost)
270
0
        return;
271
272
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::line_width(width={})", width);
273
274
    // https://www.khronos.org/registry/webgl/specs/latest/1.0/#NAN_LINE_WIDTH
275
    // "In the WebGL API, if the width parameter passed to lineWidth is set to NaN, an INVALID_VALUE error is generated and the line width is not changed."
276
0
    RETURN_WITH_WEBGL_ERROR_IF(isnan(width), GL_INVALID_VALUE);
277
0
    m_context->gl_line_width(width);
278
0
}
279
280
void WebGLRenderingContextBase::polygon_offset(GLfloat factor, GLfloat units)
281
0
{
282
0
    if (m_context_lost)
283
0
        return;
284
285
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::polygon_offset(factor={}, units={})", factor, units);
286
0
    m_context->gl_polygon_offset(factor, units);
287
0
}
288
289
void WebGLRenderingContextBase::scissor(GLint x, GLint y, GLsizei width, GLsizei height)
290
0
{
291
0
    if (m_context_lost)
292
0
        return;
293
294
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::scissor(x={}, y={}, width={}, height={})", x, y, width, height);
295
0
    m_context->gl_scissor(x, y, width, height);
296
0
}
297
298
void WebGLRenderingContextBase::stencil_op(GLenum fail, GLenum zfail, GLenum zpass)
299
0
{
300
0
    if (m_context_lost)
301
0
        return;
302
303
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::stencil_op(fail={:#08x}, zfail={:#08x}, zpass={:#08x})", fail, zfail, zpass);
304
0
    m_context->gl_stencil_op_separate(GL_FRONT_AND_BACK, fail, zfail, zpass);
305
0
}
306
307
void WebGLRenderingContextBase::stencil_op_separate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass)
308
0
{
309
0
    if (m_context_lost)
310
0
        return;
311
312
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::stencil_op_separate(face={:#08x}, fail={:#08x}, zfail={:#08x}, zpass={:#08x})", face, fail, zfail, zpass);
313
0
    m_context->gl_stencil_op_separate(face, fail, zfail, zpass);
314
0
}
315
316
void WebGLRenderingContextBase::viewport(GLint x, GLint y, GLsizei width, GLsizei height)
317
0
{
318
0
    if (m_context_lost)
319
0
        return;
320
321
0
    dbgln_if(WEBGL_CONTEXT_DEBUG, "WebGLRenderingContextBase::viewport(x={}, y={}, width={}, height={})", x, y, width, height);
322
0
    m_context->gl_viewport(x, y, width, height);
323
0
}
324
325
}