WebGLContextState.cpp (12941B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "GLContext.h" 7 #include "GLScreenBuffer.h" 8 #include "MozFramebuffer.h" 9 #include "WebGLBuffer.h" 10 #include "WebGLContext.h" 11 #include "WebGLContextUtils.h" 12 #include "WebGLFormats.h" 13 #include "WebGLFramebuffer.h" 14 #include "WebGLProgram.h" 15 #include "WebGLRenderbuffer.h" 16 #include "WebGLShader.h" 17 #include "WebGLTexture.h" 18 #include "WebGLVertexArray.h" 19 #include "mozilla/Maybe.h" 20 #include "mozilla/Preferences.h" 21 #include "nsString.h" 22 23 namespace mozilla { 24 25 void WebGLContext::SetEnabled(const GLenum cap, const Maybe<GLuint> i, 26 const bool enabled) { 27 const FuncScope funcScope(*this, "enable(i)/disable(i)"); 28 if (IsContextLost()) return; 29 30 if (!mIsEnabledMapKeys.count(cap)) { 31 MOZ_ASSERT(false, "Bad cap."); 32 return; 33 } 34 35 if (cap == LOCAL_GL_BLEND) { 36 if (i) { 37 const auto limit = MaxValidDrawBuffers(); 38 if (*i >= limit) { 39 ErrorInvalidValue("`index` (%u) must be < %s (%u)", *i, 40 "MAX_DRAW_BUFFERS", limit); 41 return; 42 } 43 mBlendEnabled[*i] = enabled; 44 } else { 45 if (enabled) { 46 mBlendEnabled.set(); 47 } else { 48 mBlendEnabled.reset(); 49 } 50 } 51 } else { 52 if (i) { 53 MOZ_ASSERT(false, "i"); 54 return; 55 } 56 const auto slot = GetStateTrackingSlot(cap); 57 if (slot) { 58 *slot = enabled; 59 } 60 } 61 62 switch (cap) { 63 case LOCAL_GL_DEPTH_TEST: 64 case LOCAL_GL_STENCIL_TEST: 65 break; // Lazily applied, so don't tell GL yet or we will desync. 66 67 default: 68 // Non-lazy caps. 69 if (i) { 70 if (enabled) { 71 gl->fEnablei(cap, *i); 72 } else { 73 gl->fDisablei(cap, *i); 74 } 75 } else { 76 gl->SetEnabled(cap, enabled); 77 } 78 break; 79 } 80 } 81 82 bool WebGLContext::GetStencilBits(GLint* const out_stencilBits) const { 83 *out_stencilBits = 0; 84 if (mBoundDrawFramebuffer) { 85 if (!mBoundDrawFramebuffer->IsCheckFramebufferStatusComplete()) { 86 // Error, we don't know which stencil buffer's bits to use 87 ErrorInvalidFramebufferOperation( 88 "getParameter: framebuffer has two stencil buffers bound"); 89 return false; 90 } 91 92 if (mBoundDrawFramebuffer->StencilAttachment().HasAttachment() || 93 mBoundDrawFramebuffer->DepthStencilAttachment().HasAttachment()) { 94 *out_stencilBits = 8; 95 } 96 } else if (mOptions.stencil) { 97 *out_stencilBits = 8; 98 } 99 100 return true; 101 } 102 103 Maybe<double> WebGLContext::GetParameter(const GLenum pname) { 104 const FuncScope funcScope(*this, "getParameter"); 105 if (IsContextLost()) return {}; 106 107 if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { 108 if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) { 109 return Some(MaxValidDrawBuffers()); 110 111 } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) { 112 return Some(MaxValidDrawBuffers()); 113 114 } else if (pname >= LOCAL_GL_DRAW_BUFFER0 && 115 pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + MaxValidDrawBuffers())) { 116 const auto slotId = pname - LOCAL_GL_DRAW_BUFFER0; 117 GLenum ret = LOCAL_GL_NONE; 118 if (!mBoundDrawFramebuffer) { 119 if (slotId == 0) { 120 ret = mDefaultFB_DrawBuffer0; 121 } 122 } else { 123 const auto& fb = *mBoundDrawFramebuffer; 124 const auto& bs = fb.DrawBufferEnabled(); 125 if (bs[slotId]) { 126 ret = LOCAL_GL_COLOR_ATTACHMENT0 + slotId; 127 } 128 } 129 return Some(ret); 130 } 131 } 132 133 if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) { 134 switch (pname) { 135 case LOCAL_GL_TIMESTAMP_EXT: { 136 uint64_t val = 0; 137 if (Has64BitTimestamps()) { 138 gl->fGetInteger64v(pname, (GLint64*)&val); 139 } else { 140 gl->fGetIntegerv(pname, (GLint*)&val); 141 } 142 // TODO: JS doesn't support 64-bit integers. Be lossy and 143 // cast to double (53 bits) 144 return Some(val); 145 } 146 147 case LOCAL_GL_GPU_DISJOINT_EXT: { 148 realGLboolean val = false; // Not disjoint by default. 149 if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) { 150 gl->fGetBooleanv(pname, &val); 151 } 152 return Some(bool(val)); 153 } 154 155 default: 156 break; 157 } 158 } 159 160 if (IsWebGL2() || 161 IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) { 162 if (pname == LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT) { 163 GLint i = 0; 164 gl->fGetIntegerv(pname, &i); 165 return Some(i); 166 } 167 } 168 169 if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) { 170 if (pname == LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT) { 171 GLfloat f = 0.f; 172 gl->fGetFloatv(pname, &f); 173 return Some(f); 174 } 175 } 176 177 if (IsExtensionEnabled(WebGLExtensionID::MOZ_debug)) { 178 if (pname == dom::MOZ_debug_Binding::DOES_INDEX_VALIDATION) { 179 return Some(mNeedsIndexValidation); 180 } 181 } 182 183 switch (pname) { 184 //////////////////////////////// 185 // Single-value params 186 187 // unsigned int 188 case LOCAL_GL_CULL_FACE_MODE: 189 case LOCAL_GL_FRONT_FACE: 190 case LOCAL_GL_ACTIVE_TEXTURE: 191 case LOCAL_GL_STENCIL_FUNC: 192 case LOCAL_GL_STENCIL_FAIL: 193 case LOCAL_GL_STENCIL_PASS_DEPTH_FAIL: 194 case LOCAL_GL_STENCIL_PASS_DEPTH_PASS: 195 case LOCAL_GL_STENCIL_BACK_FUNC: 196 case LOCAL_GL_STENCIL_BACK_FAIL: 197 case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_FAIL: 198 case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_PASS: 199 case LOCAL_GL_DEPTH_FUNC: 200 case LOCAL_GL_BLEND_SRC_RGB: 201 case LOCAL_GL_BLEND_SRC_ALPHA: 202 case LOCAL_GL_BLEND_DST_RGB: 203 case LOCAL_GL_BLEND_DST_ALPHA: 204 case LOCAL_GL_BLEND_EQUATION_RGB: 205 case LOCAL_GL_BLEND_EQUATION_ALPHA: { 206 GLint i = 0; 207 gl->fGetIntegerv(pname, &i); 208 return Some(i); 209 } 210 211 case LOCAL_GL_GENERATE_MIPMAP_HINT: 212 return Some(mGenerateMipmapHint); 213 214 case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT: 215 case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: { 216 const webgl::FormatUsageInfo* usage; 217 uint32_t width, height; 218 if (!BindCurFBForColorRead(&usage, &width, &height, 219 LOCAL_GL_INVALID_OPERATION)) 220 return Nothing(); 221 222 const auto implPI = ValidImplementationColorReadPI(usage); 223 224 GLenum ret; 225 if (pname == LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT) { 226 ret = implPI.format; 227 } else { 228 ret = implPI.type; 229 } 230 return Some(ret); 231 } 232 233 // int 234 case LOCAL_GL_STENCIL_REF: 235 case LOCAL_GL_STENCIL_BACK_REF: { 236 GLint stencilBits = 0; 237 if (!GetStencilBits(&stencilBits)) return Nothing(); 238 239 // Assuming stencils have 8 bits 240 const GLint stencilMask = (1 << stencilBits) - 1; 241 242 GLint refValue = 0; 243 gl->fGetIntegerv(pname, &refValue); 244 245 return Some(refValue & stencilMask); 246 } 247 248 case LOCAL_GL_SAMPLE_BUFFERS: 249 case LOCAL_GL_SAMPLES: { 250 const auto& fb = mBoundDrawFramebuffer; 251 auto samples = [&]() -> Maybe<uint32_t> { 252 if (!fb) { 253 if (!EnsureDefaultFB()) return Nothing(); 254 return Some(mDefaultFB->mSamples); 255 } 256 257 if (!fb->IsCheckFramebufferStatusComplete()) return Some(0); 258 259 DoBindFB(fb, LOCAL_GL_FRAMEBUFFER); 260 return Some(gl->GetIntAs<uint32_t>(LOCAL_GL_SAMPLES)); 261 }(); 262 if (samples && pname == LOCAL_GL_SAMPLE_BUFFERS) { 263 samples = Some(uint32_t(bool(samples.value()))); 264 } 265 if (!samples) return Nothing(); 266 return Some(samples.value()); 267 } 268 269 case LOCAL_GL_STENCIL_CLEAR_VALUE: 270 case LOCAL_GL_SUBPIXEL_BITS: { 271 GLint i = 0; 272 gl->fGetIntegerv(pname, &i); 273 return Some(i); 274 } 275 276 case LOCAL_GL_RED_BITS: 277 case LOCAL_GL_GREEN_BITS: 278 case LOCAL_GL_BLUE_BITS: 279 case LOCAL_GL_ALPHA_BITS: 280 case LOCAL_GL_DEPTH_BITS: 281 case LOCAL_GL_STENCIL_BITS: { 282 const auto format = [&]() -> const webgl::FormatInfo* { 283 const auto& fb = mBoundDrawFramebuffer; 284 if (fb) { 285 if (!fb->IsCheckFramebufferStatusComplete()) return nullptr; 286 287 const auto& attachment = [&]() -> const auto& { 288 switch (pname) { 289 case LOCAL_GL_DEPTH_BITS: 290 if (fb->DepthStencilAttachment().HasAttachment()) 291 return fb->DepthStencilAttachment(); 292 return fb->DepthAttachment(); 293 294 case LOCAL_GL_STENCIL_BITS: 295 if (fb->DepthStencilAttachment().HasAttachment()) 296 return fb->DepthStencilAttachment(); 297 return fb->StencilAttachment(); 298 299 default: 300 return fb->ColorAttachment0(); 301 } 302 }(); 303 304 const auto imageInfo = attachment.GetImageInfo(); 305 if (!imageInfo) return nullptr; 306 return imageInfo->mFormat->format; 307 } 308 309 auto effFormat = webgl::EffectiveFormat::RGB8; 310 switch (pname) { 311 case LOCAL_GL_DEPTH_BITS: 312 if (mOptions.depth) { 313 effFormat = webgl::EffectiveFormat::DEPTH24_STENCIL8; 314 } 315 break; 316 317 case LOCAL_GL_STENCIL_BITS: 318 if (mOptions.stencil) { 319 effFormat = webgl::EffectiveFormat::DEPTH24_STENCIL8; 320 } 321 break; 322 323 default: 324 if (mOptions.alpha) { 325 effFormat = webgl::EffectiveFormat::RGBA8; 326 } 327 break; 328 } 329 return webgl::GetFormat(effFormat); 330 }(); 331 int32_t ret = 0; 332 if (format) { 333 switch (pname) { 334 case LOCAL_GL_RED_BITS: 335 ret = format->r; 336 break; 337 case LOCAL_GL_GREEN_BITS: 338 ret = format->g; 339 break; 340 case LOCAL_GL_BLUE_BITS: 341 ret = format->b; 342 break; 343 case LOCAL_GL_ALPHA_BITS: 344 ret = format->a; 345 break; 346 case LOCAL_GL_DEPTH_BITS: 347 ret = format->d; 348 break; 349 case LOCAL_GL_STENCIL_BITS: 350 ret = format->s; 351 break; 352 } 353 } 354 return Some(ret); 355 } 356 357 case LOCAL_GL_MAX_RENDERBUFFER_SIZE: 358 return Some(mGLMaxRenderbufferSize); 359 360 case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: 361 return Some(mGLMaxVertexTextureImageUnits); 362 363 case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS: 364 return Some(mGLMaxFragmentTextureImageUnits); 365 366 case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS: 367 return Some(mGLMaxVertexUniformVectors); 368 369 case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS: 370 return Some(mGLMaxFragmentUniformVectors); 371 372 case LOCAL_GL_MAX_VARYING_VECTORS: 373 return Some(mGLMaxFragmentInputVectors); 374 375 // unsigned int. here we may have to return very large values like 2^32-1 376 // that can't be represented as javascript integer values. We just return 377 // them as doubles and javascript doesn't care. 378 case LOCAL_GL_STENCIL_BACK_VALUE_MASK: 379 return Some(mStencilValueMaskBack); 380 // pass as FP value to allow large values such as 2^32-1. 381 382 case LOCAL_GL_STENCIL_BACK_WRITEMASK: 383 return Some(mStencilWriteMaskBack); 384 385 case LOCAL_GL_STENCIL_VALUE_MASK: 386 return Some(mStencilValueMaskFront); 387 388 case LOCAL_GL_STENCIL_WRITEMASK: 389 return Some(mStencilWriteMaskFront); 390 391 case LOCAL_GL_COLOR_WRITEMASK: 392 return Some(mColorWriteMask0); 393 394 // float 395 case LOCAL_GL_LINE_WIDTH: 396 return Some((double)mLineWidth); 397 398 case LOCAL_GL_DEPTH_CLEAR_VALUE: 399 case LOCAL_GL_POLYGON_OFFSET_FACTOR: 400 case LOCAL_GL_POLYGON_OFFSET_UNITS: 401 case LOCAL_GL_SAMPLE_COVERAGE_VALUE: { 402 GLfloat f = 0.f; 403 gl->fGetFloatv(pname, &f); 404 return Some(f); 405 } 406 407 // bool 408 case LOCAL_GL_DEPTH_TEST: 409 return Some((bool)mDepthTestEnabled); 410 case LOCAL_GL_STENCIL_TEST: 411 return Some((bool)mStencilTestEnabled); 412 413 case LOCAL_GL_BLEND: 414 case LOCAL_GL_CULL_FACE: 415 case LOCAL_GL_DITHER: 416 case LOCAL_GL_POLYGON_OFFSET_FILL: 417 case LOCAL_GL_SCISSOR_TEST: 418 case LOCAL_GL_SAMPLE_COVERAGE_INVERT: 419 case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE: 420 case LOCAL_GL_SAMPLE_COVERAGE: 421 case LOCAL_GL_DEPTH_WRITEMASK: { 422 realGLboolean b = 0; 423 gl->fGetBooleanv(pname, &b); 424 return Some(bool(b)); 425 } 426 427 default: 428 break; 429 } 430 431 ErrorInvalidEnumInfo("pname", pname); 432 return Nothing(); 433 } 434 435 bool* WebGLContext::GetStateTrackingSlot(GLenum cap) { 436 switch (cap) { 437 case LOCAL_GL_DEPTH_TEST: 438 return &mDepthTestEnabled; 439 case LOCAL_GL_DITHER: 440 return &mDitherEnabled; 441 case LOCAL_GL_RASTERIZER_DISCARD: 442 return &mRasterizerDiscardEnabled; 443 case LOCAL_GL_SCISSOR_TEST: 444 return &mScissorTestEnabled; 445 case LOCAL_GL_STENCIL_TEST: 446 return &mStencilTestEnabled; 447 } 448 449 return nullptr; 450 } 451 452 } // namespace mozilla