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