forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			483 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			483 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "WebGLContext.h"
 | |
| 
 | |
| #include "GLContext.h"
 | |
| #include "GLScreenBuffer.h"
 | |
| #include "mozilla/Maybe.h"
 | |
| #include "mozilla/Preferences.h"
 | |
| #include "MozFramebuffer.h"
 | |
| #include "nsString.h"
 | |
| #include "WebGLBuffer.h"
 | |
| #include "WebGLContextUtils.h"
 | |
| #include "WebGLFramebuffer.h"
 | |
| #include "WebGLProgram.h"
 | |
| #include "WebGLRenderbuffer.h"
 | |
| #include "WebGLShader.h"
 | |
| #include "WebGLTexture.h"
 | |
| #include "WebGLVertexArray.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| void WebGLContext::SetEnabled(const GLenum cap, const Maybe<GLuint> i,
 | |
|                               const bool enabled) {
 | |
|   const FuncScope funcScope(*this, "enable(i)/disable(i)");
 | |
|   if (IsContextLost()) return;
 | |
| 
 | |
|   if (!ValidateCapabilityEnum(cap)) return;
 | |
| 
 | |
|   if (i) {
 | |
|     if (cap != LOCAL_GL_BLEND) {
 | |
|       ErrorInvalidEnumArg("cap", cap);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     const auto limit = MaxValidDrawBuffers();
 | |
|     if (*i >= limit) {
 | |
|       ErrorInvalidValue("`index` (%u) must be < %s (%u)", *i,
 | |
|                         "MAX_DRAW_BUFFERS", limit);
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const auto slot = GetStateTrackingSlot(cap, i ? *i : 0);
 | |
|   if (slot) {
 | |
|     *slot = enabled;
 | |
|   } else if (cap == LOCAL_GL_BLEND) {
 | |
|     if (i) {
 | |
|       mBlendEnabled[*i] = enabled;
 | |
|     } else {
 | |
|       if (enabled) {
 | |
|         mBlendEnabled.set();
 | |
|       } else {
 | |
|         mBlendEnabled.reset();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   switch (cap) {
 | |
|     case LOCAL_GL_DEPTH_TEST:
 | |
|     case LOCAL_GL_STENCIL_TEST:
 | |
|       break;  // Lazily applied, so don't tell GL yet or we will desync.
 | |
| 
 | |
|     default:
 | |
|       // Non-lazy caps.
 | |
|       if (i) {
 | |
|         if (enabled) {
 | |
|           gl->fEnablei(cap, *i);
 | |
|         } else {
 | |
|           gl->fDisablei(cap, *i);
 | |
|         }
 | |
|       } else {
 | |
|         gl->SetEnabled(cap, enabled);
 | |
|       }
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool WebGLContext::GetStencilBits(GLint* const out_stencilBits) const {
 | |
|   *out_stencilBits = 0;
 | |
|   if (mBoundDrawFramebuffer) {
 | |
|     if (!mBoundDrawFramebuffer->IsCheckFramebufferStatusComplete()) {
 | |
|       // Error, we don't know which stencil buffer's bits to use
 | |
|       ErrorInvalidFramebufferOperation(
 | |
|           "getParameter: framebuffer has two stencil buffers bound");
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     if (mBoundDrawFramebuffer->StencilAttachment().HasAttachment() ||
 | |
|         mBoundDrawFramebuffer->DepthStencilAttachment().HasAttachment()) {
 | |
|       *out_stencilBits = 8;
 | |
|     }
 | |
|   } else if (mOptions.stencil) {
 | |
|     *out_stencilBits = 8;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| Maybe<double> WebGLContext::GetParameter(const GLenum pname) {
 | |
|   const FuncScope funcScope(*this, "getParameter");
 | |
|   if (IsContextLost()) return {};
 | |
| 
 | |
|   if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) {
 | |
|     if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) {
 | |
|       return Some(MaxValidDrawBuffers());
 | |
| 
 | |
|     } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) {
 | |
|       return Some(MaxValidDrawBuffers());
 | |
| 
 | |
|     } else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
 | |
|                pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + MaxValidDrawBuffers())) {
 | |
|       const auto slotId = pname - LOCAL_GL_DRAW_BUFFER0;
 | |
|       GLenum ret = LOCAL_GL_NONE;
 | |
|       if (!mBoundDrawFramebuffer) {
 | |
|         if (slotId == 0) {
 | |
|           ret = mDefaultFB_DrawBuffer0;
 | |
|         }
 | |
|       } else {
 | |
|         const auto& fb = *mBoundDrawFramebuffer;
 | |
|         const auto& bs = fb.DrawBufferEnabled();
 | |
|         if (bs[slotId]) {
 | |
|           ret = LOCAL_GL_COLOR_ATTACHMENT0 + slotId;
 | |
|         }
 | |
|       }
 | |
|       return Some(ret);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
 | |
|     switch (pname) {
 | |
|       case LOCAL_GL_TIMESTAMP_EXT: {
 | |
|         uint64_t val = 0;
 | |
|         if (Has64BitTimestamps()) {
 | |
|           gl->fGetInteger64v(pname, (GLint64*)&val);
 | |
|         } else {
 | |
|           gl->fGetIntegerv(pname, (GLint*)&val);
 | |
|         }
 | |
|         // TODO: JS doesn't support 64-bit integers. Be lossy and
 | |
|         // cast to double (53 bits)
 | |
|         return Some(val);
 | |
|       }
 | |
| 
 | |
|       case LOCAL_GL_GPU_DISJOINT_EXT: {
 | |
|         realGLboolean val = false;  // Not disjoint by default.
 | |
|         if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) {
 | |
|           gl->fGetBooleanv(pname, &val);
 | |
|         }
 | |
|         return Some(bool(val));
 | |
|       }
 | |
| 
 | |
|       default:
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (IsWebGL2() ||
 | |
|       IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) {
 | |
|     if (pname == LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT) {
 | |
|       GLint i = 0;
 | |
|       gl->fGetIntegerv(pname, &i);
 | |
|       return Some(i);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) {
 | |
|     if (pname == LOCAL_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT) {
 | |
|       GLfloat f = 0.f;
 | |
|       gl->fGetFloatv(pname, &f);
 | |
|       return Some(f);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (IsExtensionEnabled(WebGLExtensionID::MOZ_debug)) {
 | |
|     if (pname == dom::MOZ_debug_Binding::DOES_INDEX_VALIDATION) {
 | |
|       return Some(mNeedsIndexValidation);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   switch (pname) {
 | |
|     ////////////////////////////////
 | |
|     // Single-value params
 | |
| 
 | |
|     // unsigned int
 | |
|     case LOCAL_GL_CULL_FACE_MODE:
 | |
|     case LOCAL_GL_FRONT_FACE:
 | |
|     case LOCAL_GL_ACTIVE_TEXTURE:
 | |
|     case LOCAL_GL_STENCIL_FUNC:
 | |
|     case LOCAL_GL_STENCIL_FAIL:
 | |
|     case LOCAL_GL_STENCIL_PASS_DEPTH_FAIL:
 | |
|     case LOCAL_GL_STENCIL_PASS_DEPTH_PASS:
 | |
|     case LOCAL_GL_STENCIL_BACK_FUNC:
 | |
|     case LOCAL_GL_STENCIL_BACK_FAIL:
 | |
|     case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_FAIL:
 | |
|     case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_PASS:
 | |
|     case LOCAL_GL_DEPTH_FUNC:
 | |
|     case LOCAL_GL_BLEND_SRC_RGB:
 | |
|     case LOCAL_GL_BLEND_SRC_ALPHA:
 | |
|     case LOCAL_GL_BLEND_DST_RGB:
 | |
|     case LOCAL_GL_BLEND_DST_ALPHA:
 | |
|     case LOCAL_GL_BLEND_EQUATION_RGB:
 | |
|     case LOCAL_GL_BLEND_EQUATION_ALPHA: {
 | |
|       GLint i = 0;
 | |
|       gl->fGetIntegerv(pname, &i);
 | |
|       return Some(i);
 | |
|     }
 | |
| 
 | |
|     case LOCAL_GL_GENERATE_MIPMAP_HINT:
 | |
|       return Some(mGenerateMipmapHint);
 | |
| 
 | |
|     case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT:
 | |
|     case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: {
 | |
|       const webgl::FormatUsageInfo* usage;
 | |
|       uint32_t width, height;
 | |
|       if (!BindCurFBForColorRead(&usage, &width, &height,
 | |
|                                  LOCAL_GL_INVALID_OPERATION))
 | |
|         return Nothing();
 | |
| 
 | |
|       const auto implPI = ValidImplementationColorReadPI(usage);
 | |
| 
 | |
|       GLenum ret;
 | |
|       if (pname == LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT) {
 | |
|         ret = implPI.format;
 | |
|       } else {
 | |
|         ret = implPI.type;
 | |
|       }
 | |
|       return Some(ret);
 | |
|     }
 | |
| 
 | |
|     // int
 | |
|     case LOCAL_GL_STENCIL_REF:
 | |
|     case LOCAL_GL_STENCIL_BACK_REF: {
 | |
|       GLint stencilBits = 0;
 | |
|       if (!GetStencilBits(&stencilBits)) return Nothing();
 | |
| 
 | |
|       // Assuming stencils have 8 bits
 | |
|       const GLint stencilMask = (1 << stencilBits) - 1;
 | |
| 
 | |
|       GLint refValue = 0;
 | |
|       gl->fGetIntegerv(pname, &refValue);
 | |
| 
 | |
|       return Some(refValue & stencilMask);
 | |
|     }
 | |
| 
 | |
|     case LOCAL_GL_SAMPLE_BUFFERS:
 | |
|     case LOCAL_GL_SAMPLES: {
 | |
|       const auto& fb = mBoundDrawFramebuffer;
 | |
|       auto samples = [&]() -> Maybe<uint32_t> {
 | |
|         if (!fb) {
 | |
|           if (!EnsureDefaultFB()) return Nothing();
 | |
|           return Some(mDefaultFB->mSamples);
 | |
|         }
 | |
| 
 | |
|         if (!fb->IsCheckFramebufferStatusComplete()) return Some(0);
 | |
| 
 | |
|         DoBindFB(fb, LOCAL_GL_FRAMEBUFFER);
 | |
|         return Some(gl->GetIntAs<uint32_t>(LOCAL_GL_SAMPLES));
 | |
|       }();
 | |
|       if (samples && pname == LOCAL_GL_SAMPLE_BUFFERS) {
 | |
|         samples = Some(uint32_t(bool(samples.value())));
 | |
|       }
 | |
|       if (!samples) return Nothing();
 | |
|       return Some(samples.value());
 | |
|     }
 | |
| 
 | |
|     case LOCAL_GL_STENCIL_CLEAR_VALUE:
 | |
|     case LOCAL_GL_SUBPIXEL_BITS: {
 | |
|       GLint i = 0;
 | |
|       gl->fGetIntegerv(pname, &i);
 | |
|       return Some(i);
 | |
|     }
 | |
| 
 | |
|     case LOCAL_GL_RED_BITS:
 | |
|     case LOCAL_GL_GREEN_BITS:
 | |
|     case LOCAL_GL_BLUE_BITS:
 | |
|     case LOCAL_GL_ALPHA_BITS:
 | |
|     case LOCAL_GL_DEPTH_BITS:
 | |
|     case LOCAL_GL_STENCIL_BITS: {
 | |
|       const auto format = [&]() -> const webgl::FormatInfo* {
 | |
|         const auto& fb = mBoundDrawFramebuffer;
 | |
|         if (fb) {
 | |
|           if (!fb->IsCheckFramebufferStatusComplete()) return nullptr;
 | |
| 
 | |
|           const auto& attachment = [&]() -> const auto& {
 | |
|             switch (pname) {
 | |
|               case LOCAL_GL_DEPTH_BITS:
 | |
|                 if (fb->DepthStencilAttachment().HasAttachment())
 | |
|                   return fb->DepthStencilAttachment();
 | |
|                 return fb->DepthAttachment();
 | |
| 
 | |
|               case LOCAL_GL_STENCIL_BITS:
 | |
|                 if (fb->DepthStencilAttachment().HasAttachment())
 | |
|                   return fb->DepthStencilAttachment();
 | |
|                 return fb->StencilAttachment();
 | |
| 
 | |
|               default:
 | |
|                 return fb->ColorAttachment0();
 | |
|             }
 | |
|           }();
 | |
| 
 | |
|           const auto imageInfo = attachment.GetImageInfo();
 | |
|           if (!imageInfo) return nullptr;
 | |
|           return imageInfo->mFormat->format;
 | |
|         }
 | |
| 
 | |
|         auto effFormat = webgl::EffectiveFormat::RGB8;
 | |
|         switch (pname) {
 | |
|           case LOCAL_GL_DEPTH_BITS:
 | |
|             if (mOptions.depth) {
 | |
|               effFormat = webgl::EffectiveFormat::DEPTH24_STENCIL8;
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|           case LOCAL_GL_STENCIL_BITS:
 | |
|             if (mOptions.stencil) {
 | |
|               effFormat = webgl::EffectiveFormat::DEPTH24_STENCIL8;
 | |
|             }
 | |
|             break;
 | |
| 
 | |
|           default:
 | |
|             if (mOptions.alpha) {
 | |
|               effFormat = webgl::EffectiveFormat::RGBA8;
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         return webgl::GetFormat(effFormat);
 | |
|       }();
 | |
|       int32_t ret = 0;
 | |
|       if (format) {
 | |
|         switch (pname) {
 | |
|           case LOCAL_GL_RED_BITS:
 | |
|             ret = format->r;
 | |
|             break;
 | |
|           case LOCAL_GL_GREEN_BITS:
 | |
|             ret = format->g;
 | |
|             break;
 | |
|           case LOCAL_GL_BLUE_BITS:
 | |
|             ret = format->b;
 | |
|             break;
 | |
|           case LOCAL_GL_ALPHA_BITS:
 | |
|             ret = format->a;
 | |
|             break;
 | |
|           case LOCAL_GL_DEPTH_BITS:
 | |
|             ret = format->d;
 | |
|             break;
 | |
|           case LOCAL_GL_STENCIL_BITS:
 | |
|             ret = format->s;
 | |
|             break;
 | |
|         }
 | |
|       }
 | |
|       return Some(ret);
 | |
|     }
 | |
| 
 | |
|     case LOCAL_GL_MAX_RENDERBUFFER_SIZE:
 | |
|       return Some(mGLMaxRenderbufferSize);
 | |
| 
 | |
|     case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
 | |
|       return Some(mGLMaxVertexTextureImageUnits);
 | |
| 
 | |
|     case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS:
 | |
|       return Some(mGLMaxFragmentTextureImageUnits);
 | |
| 
 | |
|     case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS:
 | |
|       return Some(mGLMaxVertexUniformVectors);
 | |
| 
 | |
|     case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
 | |
|       return Some(mGLMaxFragmentUniformVectors);
 | |
| 
 | |
|     case LOCAL_GL_MAX_VARYING_VECTORS:
 | |
|       return Some(mGLMaxFragmentInputVectors);
 | |
| 
 | |
|     // unsigned int. here we may have to return very large values like 2^32-1
 | |
|     // that can't be represented as javascript integer values. We just return
 | |
|     // them as doubles and javascript doesn't care.
 | |
|     case LOCAL_GL_STENCIL_BACK_VALUE_MASK:
 | |
|       return Some(mStencilValueMaskBack);
 | |
|       // pass as FP value to allow large values such as 2^32-1.
 | |
| 
 | |
|     case LOCAL_GL_STENCIL_BACK_WRITEMASK:
 | |
|       return Some(mStencilWriteMaskBack);
 | |
| 
 | |
|     case LOCAL_GL_STENCIL_VALUE_MASK:
 | |
|       return Some(mStencilValueMaskFront);
 | |
| 
 | |
|     case LOCAL_GL_STENCIL_WRITEMASK:
 | |
|       return Some(mStencilWriteMaskFront);
 | |
| 
 | |
|     case LOCAL_GL_COLOR_WRITEMASK:
 | |
|       return Some(mColorWriteMask0);
 | |
| 
 | |
|     // float
 | |
|     case LOCAL_GL_LINE_WIDTH:
 | |
|       return Some((double)mLineWidth);
 | |
| 
 | |
|     case LOCAL_GL_DEPTH_CLEAR_VALUE:
 | |
|     case LOCAL_GL_POLYGON_OFFSET_FACTOR:
 | |
|     case LOCAL_GL_POLYGON_OFFSET_UNITS:
 | |
|     case LOCAL_GL_SAMPLE_COVERAGE_VALUE: {
 | |
|       GLfloat f = 0.f;
 | |
|       gl->fGetFloatv(pname, &f);
 | |
|       return Some(f);
 | |
|     }
 | |
| 
 | |
|     // bool
 | |
|     case LOCAL_GL_DEPTH_TEST:
 | |
|       return Some((bool)mDepthTestEnabled);
 | |
|     case LOCAL_GL_STENCIL_TEST:
 | |
|       return Some((bool)mStencilTestEnabled);
 | |
| 
 | |
|     case LOCAL_GL_BLEND:
 | |
|     case LOCAL_GL_CULL_FACE:
 | |
|     case LOCAL_GL_DITHER:
 | |
|     case LOCAL_GL_POLYGON_OFFSET_FILL:
 | |
|     case LOCAL_GL_SCISSOR_TEST:
 | |
|     case LOCAL_GL_SAMPLE_COVERAGE_INVERT:
 | |
|     case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE:
 | |
|     case LOCAL_GL_SAMPLE_COVERAGE:
 | |
|     case LOCAL_GL_DEPTH_WRITEMASK: {
 | |
|       realGLboolean b = 0;
 | |
|       gl->fGetBooleanv(pname, &b);
 | |
|       return Some(bool(b));
 | |
|     }
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   ErrorInvalidEnumInfo("pname", pname);
 | |
|   return Nothing();
 | |
| }
 | |
| 
 | |
| bool WebGLContext::IsEnabled(GLenum cap) {
 | |
|   const FuncScope funcScope(*this, "isEnabled");
 | |
|   if (IsContextLost()) return false;
 | |
| 
 | |
|   if (!ValidateCapabilityEnum(cap)) return false;
 | |
| 
 | |
|   const auto& slot = GetStateTrackingSlot(cap, 0);
 | |
|   if (slot) return *slot;
 | |
| 
 | |
|   return gl->fIsEnabled(cap);
 | |
| }
 | |
| 
 | |
| bool WebGLContext::ValidateCapabilityEnum(GLenum cap) {
 | |
|   switch (cap) {
 | |
|     case LOCAL_GL_BLEND:
 | |
|     case LOCAL_GL_CULL_FACE:
 | |
|     case LOCAL_GL_DEPTH_TEST:
 | |
|     case LOCAL_GL_DITHER:
 | |
|     case LOCAL_GL_POLYGON_OFFSET_FILL:
 | |
|     case LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE:
 | |
|     case LOCAL_GL_SAMPLE_COVERAGE:
 | |
|     case LOCAL_GL_SCISSOR_TEST:
 | |
|     case LOCAL_GL_STENCIL_TEST:
 | |
|       return true;
 | |
|     case LOCAL_GL_RASTERIZER_DISCARD:
 | |
|       return IsWebGL2();
 | |
|     default:
 | |
|       ErrorInvalidEnumInfo("cap", cap);
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool* WebGLContext::GetStateTrackingSlot(GLenum cap, GLuint i) {
 | |
|   switch (cap) {
 | |
|     case LOCAL_GL_DEPTH_TEST:
 | |
|       return &mDepthTestEnabled;
 | |
|     case LOCAL_GL_DITHER:
 | |
|       return &mDitherEnabled;
 | |
|     case LOCAL_GL_RASTERIZER_DISCARD:
 | |
|       return &mRasterizerDiscardEnabled;
 | |
|     case LOCAL_GL_SCISSOR_TEST:
 | |
|       return &mScissorTestEnabled;
 | |
|     case LOCAL_GL_STENCIL_TEST:
 | |
|       return &mStencilTestEnabled;
 | |
|   }
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla
 | 
