bool WebGLContext::InitWebGL2() { MOZ_ASSERT(IsWebGL2(), "WebGLContext is not a WebGL 2 context!"); // check OpenGL features if (!gl->IsSupported(gl::GLFeature::occlusion_query) && !gl->IsSupported(gl::GLFeature::occlusion_query_boolean)) { // On desktop, we fake occlusion_query_boolean with occlusion_query if // necessary. (See WebGL2ContextQueries.cpp) GenerateWarning("WebGL 2 unavailable. Requires occlusion queries."); return false; } std::vector<gl::GLFeature> missingList; for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) { if (!gl->IsSupported(kRequiredFeatures[i])) missingList.push_back(kRequiredFeatures[i]); } if (missingList.size()) { nsAutoCString exts; for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) { exts.AppendLiteral("\n "); exts.Append(gl::GLContext::GetFeatureName(*itr)); } GenerateWarning("WebGL 2 unavailable. The following required features are" " unavailible: %s", exts.BeginReading()); return false; } // ok WebGL 2 is compatible, we can enable natively supported extensions. for (size_t i = 0; i < ArrayLength(kNativelySupportedExtensions); i++) { EnableExtension(kNativelySupportedExtensions[i]); MOZ_ASSERT(IsExtensionEnabled(kNativelySupportedExtensions[i])); } // we initialise WebGL 2 related stuff. gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &mGLMaxTransformFeedbackSeparateAttribs); gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS, &mGLMaxUniformBufferBindings); mBoundTransformFeedbackBuffers.SetLength(mGLMaxTransformFeedbackSeparateAttribs); mBoundUniformBuffers.SetLength(mGLMaxUniformBufferBindings); mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0); mBoundTransformFeedback = mDefaultTransformFeedback; mBypassShaderValidation = true; return true; }
GLenum WebGLContext::CheckedBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) { #ifdef XP_MACOSX // bug 790879 if (gl->WorkAroundDriverBugs() && int64_t(size) > INT32_MAX) // cast avoids a potential always-true warning on 32bit { GenerateWarning("Rejecting valid bufferData call with size %lu to avoid" " a Mac bug", size); return LOCAL_GL_INVALID_VALUE; } #endif WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target); WebGLBuffer* boundBuffer = bufferSlot.get(); MOZ_ASSERT(boundBuffer, "No buffer bound for this target."); bool sizeChanges = uint32_t(size) != boundBuffer->ByteLength(); if (sizeChanges) { GetAndFlushUnderlyingGLErrors(); gl->fBufferData(target, size, data, usage); GLenum error = GetAndFlushUnderlyingGLErrors(); return error; } else { gl->fBufferData(target, size, data, usage); return LOCAL_GL_NO_ERROR; } }
void WebGL2Context::DeleteQuery(WebGLQuery* query) { if (IsContextLost()) return; if (!query) return; if (query->IsDeleted()) return; if (query->IsActive()) EndQuery(query->mType); if (mActiveOcclusionQuery && !gl->IsGLES()) { /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt * * Calling either GenQueriesARB or DeleteQueriesARB while any query of * any target is active causes an INVALID_OPERATION error to be * generated. */ GenerateWarning("deleteQuery: The WebGL 2 prototype might generate" " INVALID_OPERATION when deleting a query object while" " one other is active."); } query->RequestDelete(); }
already_AddRefed<WebGLQuery> WebGL2Context::CreateQuery() { if (IsContextLost()) return nullptr; if (mActiveOcclusionQuery && !gl->IsGLES()) { /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt * * Calling either GenQueriesARB or DeleteQueriesARB while any query of * any target is active causes an INVALID_OPERATION error to be * generated. */ GenerateWarning("createQuery: The WebGL 2 prototype might generate" " INVALID_OPERATION when creating a query object while" " one other is active."); /* * We *need* to lock webgl2 to GL>=3.0 on desktop, but we don't have a * good mechanism to do this yet. See bug 898404. */ } RefPtr<WebGLQuery> globj = new WebGLQuery(this); return globj.forget(); }
void WebGLContext::GenerateWarning(const char* fmt, ...) const { va_list ap; va_start(ap, fmt); GenerateWarning(fmt, ap); va_end(ap); }
void WebGLContext::ErrorOutOfMemory(const char* fmt, ...) const { va_list va; va_start(va, fmt); GenerateWarning(fmt, va); va_end(va); return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY); }
void WebGLContext::ErrorInvalidValue(const char* fmt, ...) const { va_list va; va_start(va, fmt); GenerateWarning(fmt, va); va_end(va); return SynthesizeGLError(LOCAL_GL_INVALID_VALUE); }
void WebGLContext::ErrorInvalidFramebufferOperation(const char* fmt, ...) const { va_list va; va_start(va, fmt); GenerateWarning(fmt, va); va_end(va); return SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION); }
void WebGLContext::GenerateError(const GLenum err, const char* const fmt, ...) const { va_list va; va_start(va, fmt); GenerateWarning(fmt, va); va_end(va); return SynthesizeGLError(err); }
void WebGLContext::ErrorImplementationBug(const char* fmt, ...) const { const nsPrintfCString warning("Implementation bug, please file at %s! %s", "https://bugzilla.mozilla.org/", fmt); va_list va; va_start(va, fmt); GenerateWarning(warning.BeginReading(), va); va_end(va); MOZ_ASSERT(false, "WebGLContext::ErrorImplementationBug"); NS_ERROR("WebGLContext::ErrorImplementationBug"); return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY); }
void WebGLContext::Clear(GLbitfield mask) { const char funcName[] = "clear"; if (IsContextLost()) return; MakeContextCurrent(); uint32_t m = mask & (LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT); if (mask != m) return ErrorInvalidValue("%s: invalid mask bits", funcName); if (mask == 0) { GenerateWarning("Calling gl.clear(0) has no effect."); } else if (mRasterizerDiscardEnabled) { GenerateWarning("Calling gl.clear() with RASTERIZER_DISCARD enabled has no effects."); } if (mBoundDrawFramebuffer) { if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName)) return; gl->fClear(mask); return; } else { ClearBackbufferIfNeeded(); } // Ok, we're clearing the default framebuffer/screen. { ScopedMaskWorkaround autoMask(*this); gl->fClear(mask); } Invalidate(); mShouldPresent = true; }
void WebGLContext::Draw_cleanup(const char* funcName) { UndoFakeVertexAttrib0(); if (!mBoundDrawFramebuffer) { Invalidate(); mShouldPresent = true; MOZ_ASSERT(!mBackbufferNeedsClear); } if (gl->WorkAroundDriverBugs()) { if (gl->Renderer() == gl::GLRenderer::Tegra) { mDrawCallsSinceLastFlush++; if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) { gl->fFlush(); mDrawCallsSinceLastFlush = 0; } } } // Let's check for a really common error: Viewport is larger than the actual // destination framebuffer. uint32_t destWidth = mViewportWidth; uint32_t destHeight = mViewportHeight; if (mBoundDrawFramebuffer) { const auto& fba = mBoundDrawFramebuffer->ColorAttachment(0); if (fba.IsDefined()) { fba.Size(&destWidth, &destHeight); } } else { destWidth = mWidth; destHeight = mHeight; } if (mViewportWidth > int32_t(destWidth) || mViewportHeight > int32_t(destHeight)) { if (!mAlreadyWarnedAboutViewportLargerThanDest) { GenerateWarning("%s: Drawing to a destination rect smaller than the viewport" " rect. (This warning will only be given once)", funcName); mAlreadyWarnedAboutViewportLargerThanDest = true; } } }
void WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage) { if (IsContextLost()) return; if (!ValidateBufferTarget(target, "bufferData")) return; WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target); if (size < 0) return ErrorInvalidValue("bufferData: negative size"); if (!ValidateBufferUsageEnum(usage, "bufferData: usage")) return; // careful: WebGLsizeiptr is always 64-bit, but GLsizeiptr is like intptr_t. if (!CheckedInt<GLsizeiptr>(size).isValid()) return ErrorOutOfMemory("bufferData: bad size"); WebGLBuffer* boundBuffer = bufferSlot.get(); if (!boundBuffer) return ErrorInvalidOperation("bufferData: no buffer bound!"); UniquePtr<uint8_t> zeroBuffer((uint8_t*)calloc(size, 1)); if (!zeroBuffer) return ErrorOutOfMemory("bufferData: out of memory"); MakeContextCurrent(); InvalidateBufferFetching(); GLenum error = CheckedBufferData(target, size, zeroBuffer.get(), usage); if (error) { GenerateWarning("bufferData generated error %s", ErrorName(error)); return; } boundBuffer->SetByteLength(size); if (!boundBuffer->ElementArrayCacheBufferData(nullptr, size)) { return ErrorOutOfMemory("bufferData: out of memory"); } }
void WebGLContext::BufferDataT(GLenum target, const BufferT& data, GLenum usage) { if (IsContextLost()) return; if (!ValidateBufferTarget(target, "bufferData")) return; const WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target); data.ComputeLengthAndData(); // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr // is like intptr_t. if (!CheckedInt<GLsizeiptr>(data.Length()).isValid()) return ErrorOutOfMemory("bufferData: bad size"); if (!ValidateBufferUsageEnum(usage, "bufferData: usage")) return; WebGLBuffer* boundBuffer = bufferSlot.get(); if (!boundBuffer) return ErrorInvalidOperation("bufferData: no buffer bound!"); MakeContextCurrent(); InvalidateBufferFetching(); GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage); if (error) { GenerateWarning("bufferData generated error %s", ErrorName(error)); return; } boundBuffer->SetByteLength(data.Length()); if (!boundBuffer->ElementArrayCacheBufferData(data.Data(), data.Length())) return ErrorOutOfMemory("bufferData: out of memory"); }
void WebGL2Context::GetInternalformatParameter(JSContext*, GLenum target, GLenum internalformat, GLenum pname, JS::MutableHandleValue retval) { GenerateWarning("getInternalformatParameter: Not Implemented."); }
char *CNmcContext::GetNextToken () { // // If we have no stream, return nothing // if (m_pStreamTop == NULL) return NULL; // // If we pushed our current token, then return it // if (m_pStreamTop ->nTokenState == TokenState_Pushed) { m_pStreamTop ->nTokenState = TokenState_Current; return m_pStreamTop ->pszToken; } // // Otherwise, we need to get the next token // else { // // Initialize the new state // m_pStreamTop ->nTokenState = TokenState_None; assert (m_pStreamTop ->pszNextTokenPos != NULL); // // If we are at the end of a line, then return NULL // if (m_pStreamTop ->pszNextTokenPos == NULL) return NULL; // // Locate the first non-space character // while (*m_pStreamTop ->pszNextTokenPos != 0) { if (!isspace (*m_pStreamTop ->pszNextTokenPos)) break; m_pStreamTop ->pszNextTokenPos++; } // // Copy the token into the token buffer // bool fWarned = false; int nChars = 0; while (*m_pStreamTop ->pszNextTokenPos != 0) { if (isspace (*m_pStreamTop ->pszNextTokenPos)) break; if (nChars >= Max_Token_Length - 1) { if (!fWarned) { GenerateWarning ("Token too long."); fWarned = true; } } else { m_pStreamTop ->pszToken [nChars++] = *m_pStreamTop ->pszNextTokenPos; } m_pStreamTop ->pszNextTokenPos++; } m_pStreamTop ->pszToken [nChars] = 0; // // If the token is NULL, then return NULL. Otherwise, // update our current state and return the token // if (m_pStreamTop ->pszToken [0] == 0) return NULL; else { m_pStreamTop ->nTokenState = TokenState_Current; return m_pStreamTop ->pszToken; } } }
void WebGLContext::GetExtension(JSContext* cx, const nsAString& wideName, JS::MutableHandle<JSObject*> retval, ErrorResult& rv) { retval.set(nullptr); if (IsContextLost()) return; NS_LossyConvertUTF16toASCII name(wideName); WebGLExtensionID ext = WebGLExtensionID::Unknown; // step 1: figure what extension is wanted for (size_t i = 0; i < size_t(WebGLExtensionID::Max); i++) { WebGLExtensionID extension = WebGLExtensionID(i); if (CompareWebGLExtensionName(name, GetExtensionString(extension))) { ext = extension; break; } } if (ext == WebGLExtensionID::Unknown) { // We keep backward compatibility for these deprecated vendor-prefixed // alias. Do not add new ones anymore. Hide it behind the // webgl.enable-draft-extensions flag instead. if (CompareWebGLExtensionName(name, "MOZ_WEBGL_lose_context")) { ext = WebGLExtensionID::WEBGL_lose_context; } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_s3tc")) { ext = WebGLExtensionID::WEBGL_compressed_texture_s3tc; } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_atc")) { ext = WebGLExtensionID::WEBGL_compressed_texture_atc; } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_pvrtc")) { ext = WebGLExtensionID::WEBGL_compressed_texture_pvrtc; } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_depth_texture")) { ext = WebGLExtensionID::WEBGL_depth_texture; } if (ext != WebGLExtensionID::Unknown) { GenerateWarning("getExtension('%s'): MOZ_ prefixed WebGL extension" " strings are deprecated. Support for them will be" " removed in the future. Use unprefixed extension" " strings. To get draft extensions, set the" " webgl.enable-draft-extensions preference.", name.get()); } } if (ext == WebGLExtensionID::Unknown) return; // step 2: check if the extension is supported if (!IsExtensionSupported(cx, ext)) return; // step 3: if the extension hadn't been previously been created, create it now, thus enabling it WebGLExtensionBase* extObj = EnableSupportedExtension(cx, ext); if (!extObj) return; // Step 4: Enable any implied extensions. switch (ext) { case WebGLExtensionID::OES_texture_float: EnableSupportedExtension(cx, WebGLExtensionID::WEBGL_color_buffer_float); break; case WebGLExtensionID::OES_texture_half_float: EnableSupportedExtension(cx, WebGLExtensionID::EXT_color_buffer_half_float); break; default: break; } retval.set(WebGLObjectAsJSObject(cx, extObj, rv)); }
JS::Value WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) { const char funcName[] = "getParameter"; if (IsContextLost()) return JS::NullValue(); MakeContextCurrent(); if (MinCapabilityMode()) { switch(pname) { //////////////////////////// // Single-value params // int case LOCAL_GL_MAX_VERTEX_ATTRIBS: return JS::Int32Value(MINVALUE_GL_MAX_VERTEX_ATTRIBS); case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS: return JS::Int32Value(MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS); case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS: return JS::Int32Value(MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS); case LOCAL_GL_MAX_VARYING_VECTORS: return JS::Int32Value(MINVALUE_GL_MAX_VARYING_VECTORS); case LOCAL_GL_MAX_TEXTURE_SIZE: return JS::Int32Value(MINVALUE_GL_MAX_TEXTURE_SIZE); case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE: return JS::Int32Value(MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE); case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS: return JS::Int32Value(MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS); case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: return JS::Int32Value(MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS); case LOCAL_GL_MAX_RENDERBUFFER_SIZE: return JS::Int32Value(MINVALUE_GL_MAX_RENDERBUFFER_SIZE); default: // Return the real value; we're not overriding this one break; } } if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) { return JS::Int32Value(mImplMaxColorAttachments); } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) { return JS::Int32Value(mImplMaxDrawBuffers); } else if (pname >= LOCAL_GL_DRAW_BUFFER0 && pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mImplMaxDrawBuffers)) { GLint iv = 0; gl->fGetIntegerv(pname, &iv); if (mBoundDrawFramebuffer) return JS::Int32Value(iv); const GLint index = (pname - LOCAL_GL_DRAW_BUFFER0); if (iv == LOCAL_GL_COLOR_ATTACHMENT0 + index) return JS::Int32Value(LOCAL_GL_BACK); return JS::Int32Value(LOCAL_GL_NONE); } } if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) { if (pname == LOCAL_GL_VERTEX_ARRAY_BINDING) { WebGLVertexArray* vao = (mBoundVertexArray != mDefaultVertexArray) ? mBoundVertexArray.get() : nullptr; return WebGLObjectAsJSValue(cx, vao, rv); } } if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) { if (pname == LOCAL_GL_TIMESTAMP_EXT) { GLuint64 iv = 0; if (HasTimestampBits()) { gl->fGetInteger64v(pname, (GLint64*)&iv); } else { GenerateWarning("QUERY_COUNTER_BITS_EXT for TIMESTAMP_EXT is 0."); } // TODO: JS doesn't support 64-bit integers. Be lossy and // cast to double (53 bits) return JS::NumberValue(static_cast<double>(iv)); } else if (pname == LOCAL_GL_GPU_DISJOINT_EXT) { // When disjoint isn't supported, leave as false. realGLboolean disjoint = LOCAL_GL_FALSE; if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) { gl->fGetBooleanv(pname, &disjoint); } return JS::BooleanValue(bool(disjoint)); } } // Privileged string params exposed by WEBGL_debug_renderer_info. // The privilege check is done in WebGLContext::IsExtensionSupported. // So here we just have to check that the extension is enabled. if (IsExtensionEnabled(WebGLExtensionID::WEBGL_debug_renderer_info)) { switch (pname) { case UNMASKED_VENDOR_WEBGL: case UNMASKED_RENDERER_WEBGL: { const char* overridePref = nullptr; GLenum driverEnum = LOCAL_GL_NONE; switch (pname) { case UNMASKED_RENDERER_WEBGL: overridePref = "webgl.renderer-string-override"; driverEnum = LOCAL_GL_RENDERER; break; case UNMASKED_VENDOR_WEBGL: overridePref = "webgl.vendor-string-override"; driverEnum = LOCAL_GL_VENDOR; break; default: MOZ_CRASH("GFX: bad `pname`"); } bool hasRetVal = false; nsAutoString ret; if (overridePref) { nsresult res = Preferences::GetString(overridePref, &ret); if (NS_SUCCEEDED(res) && ret.Length() > 0) hasRetVal = true; } if (!hasRetVal) { const char* chars = reinterpret_cast<const char*>(gl->fGetString(driverEnum)); ret = NS_ConvertASCIItoUTF16(chars); hasRetVal = true; } return StringValue(cx, ret, rv); } } } if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) { if (pname == LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT) { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::Int32Value(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 JS::NumberValue(f); } } switch (pname) { // // String params // case LOCAL_GL_VENDOR: case LOCAL_GL_RENDERER: return StringValue(cx, "Mozilla", rv); case LOCAL_GL_VERSION: return StringValue(cx, "WebGL 1.0", rv); case LOCAL_GL_SHADING_LANGUAGE_VERSION: return StringValue(cx, "WebGL GLSL ES 1.0", rv); //////////////////////////////// // 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: case LOCAL_GL_GENERATE_MIPMAP_HINT: { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::NumberValue(uint32_t(i)); } case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: { const webgl::FormatUsageInfo* usage; uint32_t width, height; if (!ValidateCurFBForRead(funcName, &usage, &width, &height)) return JS::NullValue(); GLint i = 0; if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { gl->fGetIntegerv(pname, &i); } else { i = LOCAL_GL_UNSIGNED_BYTE; } return JS::NumberValue(uint32_t(i)); } case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT: { const webgl::FormatUsageInfo* usage; uint32_t width, height; if (!ValidateCurFBForRead(funcName, &usage, &width, &height)) return JS::NullValue(); GLint i = 0; if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { gl->fGetIntegerv(pname, &i); } else { i = LOCAL_GL_RGBA; } // OpenGL ES 3.0.4 p112 Table 3.2 shows that read format SRGB_ALPHA is // not supported. And if internal format of fbo is SRGB8_ALPHA8, then // IMPLEMENTATION_COLOR_READ_FORMAT is SRGB_ALPHA which is not supported // by ReadPixels. So, just return RGBA here. if (i == LOCAL_GL_SRGB_ALPHA) i = LOCAL_GL_RGBA; return JS::NumberValue(uint32_t(i)); } // int case LOCAL_GL_STENCIL_REF: case LOCAL_GL_STENCIL_BACK_REF: { GLint stencilBits = 0; if (!GetStencilBits(&stencilBits)) return JS::NullValue(); // Assuming stencils have 8 bits const GLint stencilMask = (1 << stencilBits) - 1; GLint refValue = 0; gl->fGetIntegerv(pname, &refValue); return JS::Int32Value(refValue & stencilMask); } case LOCAL_GL_STENCIL_CLEAR_VALUE: case LOCAL_GL_UNPACK_ALIGNMENT: case LOCAL_GL_PACK_ALIGNMENT: case LOCAL_GL_SUBPIXEL_BITS: case LOCAL_GL_SAMPLE_BUFFERS: case LOCAL_GL_SAMPLES: case LOCAL_GL_MAX_VERTEX_ATTRIBS: case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS: { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::Int32Value(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: { // Deprecated and removed in GL Core profiles, so special handling required. GLint val; if (!GetChannelBits(funcName, pname, &val)) return JS::NullValue(); return JS::Int32Value(val); } case LOCAL_GL_MAX_TEXTURE_SIZE: return JS::Int32Value(mImplMaxTextureSize); case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE: return JS::Int32Value(mImplMaxCubeMapTextureSize); case LOCAL_GL_MAX_RENDERBUFFER_SIZE: return JS::Int32Value(mImplMaxRenderbufferSize); case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS: return JS::Int32Value(mGLMaxVertexUniformVectors); case LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS: return JS::Int32Value(mGLMaxFragmentUniformVectors); case LOCAL_GL_MAX_VARYING_VECTORS: return JS::Int32Value(mGLMaxVaryingVectors); case LOCAL_GL_COMPRESSED_TEXTURE_FORMATS: { uint32_t length = mCompressedTextureFormats.Length(); JSObject* obj = dom::Uint32Array::Create(cx, this, length, mCompressedTextureFormats.Elements()); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } return JS::ObjectOrNullValue(obj); } // 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 JS::DoubleValue(mStencilValueMaskBack); // pass as FP value to allow large values such as 2^32-1. case LOCAL_GL_STENCIL_BACK_WRITEMASK: return JS::DoubleValue(mStencilWriteMaskBack); case LOCAL_GL_STENCIL_VALUE_MASK: return JS::DoubleValue(mStencilValueMaskFront); case LOCAL_GL_STENCIL_WRITEMASK: return JS::DoubleValue(mStencilWriteMaskFront); // float case LOCAL_GL_LINE_WIDTH: return JS::DoubleValue(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 JS::DoubleValue(f); } // bool case LOCAL_GL_BLEND: case LOCAL_GL_DEPTH_TEST: case LOCAL_GL_STENCIL_TEST: 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_DEPTH_WRITEMASK: { realGLboolean b = 0; gl->fGetBooleanv(pname, &b); return JS::BooleanValue(bool(b)); } // bool, WebGL-specific case UNPACK_FLIP_Y_WEBGL: return JS::BooleanValue(mPixelStore_FlipY); case UNPACK_PREMULTIPLY_ALPHA_WEBGL: return JS::BooleanValue(mPixelStore_PremultiplyAlpha); // uint, WebGL-specific case UNPACK_COLORSPACE_CONVERSION_WEBGL: return JS::NumberValue(uint32_t(mPixelStore_ColorspaceConversion)); //////////////////////////////// // Complex values // 2 floats case LOCAL_GL_DEPTH_RANGE: case LOCAL_GL_ALIASED_POINT_SIZE_RANGE: case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: { GLenum driverPName = pname; if (gl->IsCoreProfile() && driverPName == LOCAL_GL_ALIASED_POINT_SIZE_RANGE) { driverPName = LOCAL_GL_POINT_SIZE_RANGE; } GLfloat fv[2] = { 0 }; gl->fGetFloatv(driverPName, fv); JSObject* obj = dom::Float32Array::Create(cx, this, 2, fv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } return JS::ObjectOrNullValue(obj); } // 4 floats case LOCAL_GL_COLOR_CLEAR_VALUE: case LOCAL_GL_BLEND_COLOR: { GLfloat fv[4] = { 0 }; gl->fGetFloatv(pname, fv); JSObject* obj = dom::Float32Array::Create(cx, this, 4, fv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } return JS::ObjectOrNullValue(obj); } // 2 ints case LOCAL_GL_MAX_VIEWPORT_DIMS: { GLint iv[2] = { 0 }; gl->fGetIntegerv(pname, iv); JSObject* obj = dom::Int32Array::Create(cx, this, 2, iv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } return JS::ObjectOrNullValue(obj); } // 4 ints case LOCAL_GL_SCISSOR_BOX: case LOCAL_GL_VIEWPORT: { GLint iv[4] = { 0 }; gl->fGetIntegerv(pname, iv); JSObject* obj = dom::Int32Array::Create(cx, this, 4, iv); if (!obj) { rv = NS_ERROR_OUT_OF_MEMORY; } return JS::ObjectOrNullValue(obj); } // 4 bools case LOCAL_GL_COLOR_WRITEMASK: { realGLboolean gl_bv[4] = { 0 }; gl->fGetBooleanv(pname, gl_bv); bool vals[4] = { bool(gl_bv[0]), bool(gl_bv[1]), bool(gl_bv[2]), bool(gl_bv[3]) }; JS::Rooted<JS::Value> arr(cx); if (!dom::ToJSValue(cx, vals, &arr)) { rv = NS_ERROR_OUT_OF_MEMORY; } return arr; } case LOCAL_GL_ARRAY_BUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundArrayBuffer.get(), rv); } case LOCAL_GL_ELEMENT_ARRAY_BUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundVertexArray->mElementArrayBuffer.get(), rv); } case LOCAL_GL_RENDERBUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundRenderbuffer.get(), rv); } // DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING. case LOCAL_GL_FRAMEBUFFER_BINDING: { return WebGLObjectAsJSValue(cx, mBoundDrawFramebuffer.get(), rv); } case LOCAL_GL_CURRENT_PROGRAM: { return WebGLObjectAsJSValue(cx, mCurrentProgram.get(), rv); } case LOCAL_GL_TEXTURE_BINDING_2D: { return WebGLObjectAsJSValue(cx, mBound2DTextures[mActiveTexture].get(), rv); } case LOCAL_GL_TEXTURE_BINDING_CUBE_MAP: { return WebGLObjectAsJSValue(cx, mBoundCubeMapTextures[mActiveTexture].get(), rv); } default: break; } ErrorInvalidEnumInfo("getParameter: parameter", pname); return JS::NullValue(); }
bool WebGLContext::DoFakeVertexAttrib0(GLuint vertexCount) { WebGLVertexAttrib0Status whatDoesAttrib0Need = WhatDoesVertexAttrib0Need(); if (MOZ_LIKELY(whatDoesAttrib0Need == WebGLVertexAttrib0Status::Default)) return true; if (!mAlreadyWarnedAboutFakeVertexAttrib0) { GenerateWarning("Drawing without vertex attrib 0 array enabled forces the browser " "to do expensive emulation work when running on desktop OpenGL " "platforms, for example on Mac. It is preferable to always draw " "with vertex attrib 0 array enabled, by using bindAttribLocation " "to bind some always-used attribute to location 0."); mAlreadyWarnedAboutFakeVertexAttrib0 = true; } CheckedUint32 checked_dataSize = CheckedUint32(vertexCount) * 4 * sizeof(GLfloat); if (!checked_dataSize.isValid()) { ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation " "with %d vertices. Try reducing the number of vertices.", vertexCount); return false; } GLuint dataSize = checked_dataSize.value(); if (!mFakeVertexAttrib0BufferObject) { gl->fGenBuffers(1, &mFakeVertexAttrib0BufferObject); } // if the VBO status is already exactly what we need, or if the only difference is that it's initialized and // we don't need it to be, then consider it OK bool vertexAttrib0BufferStatusOK = mFakeVertexAttrib0BufferStatus == whatDoesAttrib0Need || (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray && whatDoesAttrib0Need == WebGLVertexAttrib0Status::EmulatedUninitializedArray); if (!vertexAttrib0BufferStatusOK || mFakeVertexAttrib0BufferObjectSize < dataSize || mFakeVertexAttrib0BufferObjectVector[0] != mVertexAttrib0Vector[0] || mFakeVertexAttrib0BufferObjectVector[1] != mVertexAttrib0Vector[1] || mFakeVertexAttrib0BufferObjectVector[2] != mVertexAttrib0Vector[2] || mFakeVertexAttrib0BufferObjectVector[3] != mVertexAttrib0Vector[3]) { mFakeVertexAttrib0BufferStatus = whatDoesAttrib0Need; mFakeVertexAttrib0BufferObjectSize = dataSize; mFakeVertexAttrib0BufferObjectVector[0] = mVertexAttrib0Vector[0]; mFakeVertexAttrib0BufferObjectVector[1] = mVertexAttrib0Vector[1]; mFakeVertexAttrib0BufferObjectVector[2] = mVertexAttrib0Vector[2]; mFakeVertexAttrib0BufferObjectVector[3] = mVertexAttrib0Vector[3]; gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject); GetAndFlushUnderlyingGLErrors(); if (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray) { auto array = MakeUniqueFallible<GLfloat[]>(4 * vertexCount); if (!array) { ErrorOutOfMemory("Fake attrib0 array."); return false; } for(size_t i = 0; i < vertexCount; ++i) { array[4 * i + 0] = mVertexAttrib0Vector[0]; array[4 * i + 1] = mVertexAttrib0Vector[1]; array[4 * i + 2] = mVertexAttrib0Vector[2]; array[4 * i + 3] = mVertexAttrib0Vector[3]; } gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, array.get(), LOCAL_GL_DYNAMIC_DRAW); } else { gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW); } GLenum error = GetAndFlushUnderlyingGLErrors(); gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0); // note that we do this error checking and early return AFTER having restored the buffer binding above if (error) { ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation " "with %d vertices. Try reducing the number of vertices.", vertexCount); return false; } } gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject); gl->fVertexAttribPointer(0, 4, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, 0); return true; }
void WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) { GenerateWarning("framebufferTextureLayer: Not Implemented."); }
bool WebGLContext::InitAndValidateGL() { if (!gl) return false; // Unconditionally create a new format usage authority. This is // important when restoring contexts and extensions need to add // formats back into the authority. mFormatUsage = CreateFormatUsage(gl); if (!mFormatUsage) return false; GLenum error = gl->fGetError(); if (error != LOCAL_GL_NO_ERROR) { GenerateWarning("GL error 0x%x occurred during OpenGL context" " initialization, before WebGL initialization!", error); return false; } mMinCapability = gfxPrefs::WebGLMinCapabilityMode(); mDisableExtensions = gfxPrefs::WebGLDisableExtensions(); mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure(); mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground(); mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible(); if (MinCapabilityMode()) mDisableFragHighP = true; // These are the default values, see 6.2 State tables in the // OpenGL ES 2.0.25 spec. mColorWriteMask[0] = 1; mColorWriteMask[1] = 1; mColorWriteMask[2] = 1; mColorWriteMask[3] = 1; mDepthWriteMask = 1; mColorClearValue[0] = 0.f; mColorClearValue[1] = 0.f; mColorClearValue[2] = 0.f; mColorClearValue[3] = 0.f; mDepthClearValue = 1.f; mStencilClearValue = 0; mStencilRefFront = 0; mStencilRefBack = 0; /* // Technically, we should be setting mStencil[...] values to // `allOnes`, but either ANGLE breaks or the SGX540s on Try break. GLuint stencilBits = 0; gl->GetUIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits); GLuint allOnes = ~(UINT32_MAX << stencilBits); mStencilValueMaskFront = allOnes; mStencilValueMaskBack = allOnes; mStencilWriteMaskFront = allOnes; mStencilWriteMaskBack = allOnes; */ gl->GetUIntegerv(LOCAL_GL_STENCIL_VALUE_MASK, &mStencilValueMaskFront); gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_VALUE_MASK, &mStencilValueMaskBack); gl->GetUIntegerv(LOCAL_GL_STENCIL_WRITEMASK, &mStencilWriteMaskFront); gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK, &mStencilWriteMaskBack); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK, mStencilValueMaskFront); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK, mStencilValueMaskBack); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK, mStencilWriteMaskFront); AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK, mStencilWriteMaskBack); mDitherEnabled = true; mRasterizerDiscardEnabled = false; mScissorTestEnabled = false; // Bindings, etc. mActiveTexture = 0; mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK; mEmitContextLostErrorOnce = true; mWebGLError = LOCAL_GL_NO_ERROR; mUnderlyingGLError = LOCAL_GL_NO_ERROR; mBound2DTextures.Clear(); mBoundCubeMapTextures.Clear(); mBound3DTextures.Clear(); mBound2DArrayTextures.Clear(); mBoundSamplers.Clear(); mBoundArrayBuffer = nullptr; mBoundTransformFeedbackBuffer = nullptr; mCurrentProgram = nullptr; mBoundDrawFramebuffer = nullptr; mBoundReadFramebuffer = nullptr; mBoundRenderbuffer = nullptr; MakeContextCurrent(); // For OpenGL compat. profiles, we always keep vertex attrib 0 array enabled. if (gl->IsCompatibilityProfile()) gl->fEnableVertexAttribArray(0); if (MinCapabilityMode()) mGLMaxVertexAttribs = MINVALUE_GL_MAX_VERTEX_ATTRIBS; else gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &mGLMaxVertexAttribs); if (mGLMaxVertexAttribs < 8) { GenerateWarning("GL_MAX_VERTEX_ATTRIBS: %d is < 8!", mGLMaxVertexAttribs); return false; } // Note: GL_MAX_TEXTURE_UNITS is fixed at 4 for most desktop hardware, // even though the hardware supports much more. The // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS value is the accurate value. if (MinCapabilityMode()) mGLMaxTextureUnits = MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS; else gl->fGetIntegerv(LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGLMaxTextureUnits); if (mGLMaxTextureUnits < 8) { GenerateWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d is < 8!", mGLMaxTextureUnits); return false; } mBound2DTextures.SetLength(mGLMaxTextureUnits); mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits); mBound3DTextures.SetLength(mGLMaxTextureUnits); mBound2DArrayTextures.SetLength(mGLMaxTextureUnits); mBoundSamplers.SetLength(mGLMaxTextureUnits); //////////////// if (MinCapabilityMode()) { mImplMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE; mImplMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE; mImplMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE; mImplMax3DTextureSize = MINVALUE_GL_MAX_3D_TEXTURE_SIZE; mImplMaxArrayTextureLayers = MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS; mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS; mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; } else { gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mImplMaxTextureSize); gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&mImplMaxCubeMapTextureSize); gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, (GLint*)&mImplMaxRenderbufferSize); if (!gl->GetPotentialInteger(LOCAL_GL_MAX_3D_TEXTURE_SIZE, (GLint*)&mImplMax3DTextureSize)) mImplMax3DTextureSize = 0; if (!gl->GetPotentialInteger(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS, (GLint*)&mImplMaxArrayTextureLayers)) mImplMaxArrayTextureLayers = 0; gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits); gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits); } // If we don't support a target, its max size is 0. We should only floor-to-POT if the // value if it's non-zero. (NB log2(0) is -Inf, so zero isn't an integer power-of-two) const auto fnFloorPOTIfSupported = [](uint32_t& val) { if (val) { val = FloorPOT(val); } }; fnFloorPOTIfSupported(mImplMaxTextureSize); fnFloorPOTIfSupported(mImplMaxCubeMapTextureSize); fnFloorPOTIfSupported(mImplMaxRenderbufferSize); fnFloorPOTIfSupported(mImplMax3DTextureSize); fnFloorPOTIfSupported(mImplMaxArrayTextureLayers); //////////////// mGLMaxColorAttachments = 1; mGLMaxDrawBuffers = 1; gl->GetPotentialInteger(LOCAL_GL_MAX_COLOR_ATTACHMENTS, (GLint*)&mGLMaxColorAttachments); gl->GetPotentialInteger(LOCAL_GL_MAX_DRAW_BUFFERS, (GLint*)&mGLMaxDrawBuffers); if (MinCapabilityMode()) { mGLMaxColorAttachments = std::min(mGLMaxColorAttachments, kMinMaxColorAttachments); mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, kMinMaxDrawBuffers); } if (IsWebGL2()) { mImplMaxColorAttachments = mGLMaxColorAttachments; mImplMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mImplMaxColorAttachments); } else { mImplMaxColorAttachments = 1; mImplMaxDrawBuffers = 1; } //////////////// if (MinCapabilityMode()) { mGLMaxFragmentUniformVectors = MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS; mGLMaxVertexUniformVectors = MINVALUE_GL_MAX_VERTEX_UNIFORM_VECTORS; mGLMaxVaryingVectors = MINVALUE_GL_MAX_VARYING_VECTORS; } else { if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGLMaxFragmentUniformVectors); gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS, &mGLMaxVertexUniformVectors); gl->fGetIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &mGLMaxVaryingVectors); } else { gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &mGLMaxFragmentUniformVectors); mGLMaxFragmentUniformVectors /= 4; gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS, &mGLMaxVertexUniformVectors); mGLMaxVertexUniformVectors /= 4; /* We are now going to try to read GL_MAX_VERTEX_OUTPUT_COMPONENTS * and GL_MAX_FRAGMENT_INPUT_COMPONENTS, however these constants * only entered the OpenGL standard at OpenGL 3.2. So we will try * reading, and check OpenGL error for INVALID_ENUM. * * On the public_webgl list, "problematic GetParameter pnames" * thread, the following formula was given: * maxVaryingVectors = min(GL_MAX_VERTEX_OUTPUT_COMPONENTS, * GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4 */ GLint maxVertexOutputComponents = 0; GLint maxFragmentInputComponents = 0; const bool ok = (gl->GetPotentialInteger(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS, &maxVertexOutputComponents) && gl->GetPotentialInteger(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS, &maxFragmentInputComponents)); if (ok) { mGLMaxVaryingVectors = std::min(maxVertexOutputComponents, maxFragmentInputComponents) / 4; } else { mGLMaxVaryingVectors = 16; // 16 = 64/4, and 64 is the min value for // maxVertexOutputComponents in the OpenGL 3.2 spec. } } } if (gl->IsCompatibilityProfile()) { // gl_PointSize is always available in ES2 GLSL, but has to be // specifically enabled on desktop GLSL. gl->fEnable(LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE); /* gl_PointCoord is always available in ES2 GLSL and in newer desktop * GLSL versions, but apparently not in OpenGL 2 and apparently not (due * to a driver bug) on certain NVIDIA setups. See: * http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=261472 * * Note that this used to cause crashes on old ATI drivers... Hopefully * not a significant anymore. See bug 602183. */ gl->fEnable(LOCAL_GL_POINT_SPRITE); } #ifdef XP_MACOSX if (gl->WorkAroundDriverBugs() && gl->Vendor() == gl::GLVendor::ATI && !nsCocoaFeatures::IsAtLeastVersion(10,9)) { // The Mac ATI driver, in all known OSX version up to and including // 10.8, renders points sprites upside-down. (Apple bug 11778921) gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN, LOCAL_GL_LOWER_LEFT); } #endif if (gl->IsSupported(gl::GLFeature::seamless_cube_map_opt_in)) { gl->fEnable(LOCAL_GL_TEXTURE_CUBE_MAP_SEAMLESS); } // Check the shader validator pref mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator(); // initialize shader translator if (!ShInitialize()) { GenerateWarning("GLSL translator initialization failed!"); return false; } // Mesa can only be detected with the GL_VERSION string, of the form // "2.1 Mesa 7.11.0" const char* versionStr = (const char*)(gl->fGetString(LOCAL_GL_VERSION)); mIsMesa = strstr(versionStr, "Mesa"); // Notice that the point of calling fGetError here is not only to check for // errors, but also to reset the error flags so that a subsequent WebGL // getError call will give the correct result. error = gl->fGetError(); if (error != LOCAL_GL_NO_ERROR) { GenerateWarning("GL error 0x%x occurred during WebGL context" " initialization!", error); return false; } if (IsWebGL2() && !InitWebGL2()) { // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL. return false; } // Default value for all disabled vertex attributes is [0, 0, 0, 1] mVertexAttribType = MakeUnique<GLenum[]>(mGLMaxVertexAttribs); for (int32_t index = 0; index < mGLMaxVertexAttribs; ++index) { mVertexAttribType[index] = LOCAL_GL_FLOAT; VertexAttrib4f(index, 0, 0, 0, 1); } mDefaultVertexArray = WebGLVertexArray::Create(this); mDefaultVertexArray->mAttribs.SetLength(mGLMaxVertexAttribs); mBoundVertexArray = mDefaultVertexArray; // OpenGL core profiles remove the default VAO object from version // 4.0.0. We create a default VAO for all core profiles, // regardless of version. // // GL Spec 4.0.0: // (https://www.opengl.org/registry/doc/glspec40.core.20100311.pdf) // in Section E.2.2 "Removed Features", pg 397: "[...] The default // vertex array object (the name zero) is also deprecated. [...]" if (gl->IsCoreProfile()) { MakeContextCurrent(); mDefaultVertexArray->GenVertexArray(); mDefaultVertexArray->BindVertexArray(); } mPixelStore_FlipY = false; mPixelStore_PremultiplyAlpha = false; mPixelStore_ColorspaceConversion = BROWSER_DEFAULT_WEBGL; // GLES 3.0.4, p259: mPixelStore_UnpackImageHeight = 0; mPixelStore_UnpackSkipImages = 0; mPixelStore_UnpackRowLength = 0; mPixelStore_UnpackSkipRows = 0; mPixelStore_UnpackSkipPixels = 0; mPixelStore_UnpackAlignment = 4; mPixelStore_PackRowLength = 0; mPixelStore_PackSkipRows = 0; mPixelStore_PackSkipPixels = 0; mPixelStore_PackAlignment = 4; return true; }
bool WebGLContext::DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOffset, GLsizei primcount, const char* info, GLuint* out_upperBound) { if (count < 0 || byteOffset < 0) { ErrorInvalidValue("%s: negative count or offset", info); return false; } if (primcount < 0) { ErrorInvalidValue("%s: negative primcount", info); return false; } if (!ValidateStencilParamsForDrawCall()) { return false; } // If count is 0, there's nothing to do. if (count == 0 || primcount == 0) return false; uint8_t bytesPerElem = 0; switch (type) { case LOCAL_GL_UNSIGNED_BYTE: bytesPerElem = 1; break; case LOCAL_GL_UNSIGNED_SHORT: bytesPerElem = 2; break; case LOCAL_GL_UNSIGNED_INT: if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) { bytesPerElem = 4; } break; } if (!bytesPerElem) { ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", info, type); return false; } if (byteOffset % bytesPerElem != 0) { ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`", info); return false; } const GLsizei first = byteOffset / bytesPerElem; const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count); if (!checked_byteCount.isValid()) { ErrorInvalidValue("%s: overflow in byteCount", info); return false; } // Any checks below this depend on a program being available. if (!mCurrentProgram) { ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info); return false; } if (!mBoundVertexArray->mElementArrayBuffer) { ErrorInvalidOperation("%s: must have element array buffer binding", info); return false; } WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer; if (!elemArrayBuffer.ByteLength()) { ErrorInvalidOperation("%s: bound element array buffer doesn't have any data", info); return false; } CheckedInt<GLsizei> checked_neededByteCount = checked_byteCount.toChecked<GLsizei>() + byteOffset; if (!checked_neededByteCount.isValid()) { ErrorInvalidOperation("%s: overflow in byteOffset+byteCount", info); return false; } if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) { ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info); return false; } if (!ValidateBufferFetching(info)) return false; if (!mMaxFetchedVertices || !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, count, out_upperBound)) { ErrorInvalidOperation( "%s: bound vertex attribute buffers do not have sufficient " "size for given indices from the bound element array", info); return false; } if (uint32_t(primcount) > mMaxFetchedInstances) { ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info); return false; } // Bug 1008310 - Check if buffer has been used with a different previous type if (elemArrayBuffer.IsElementArrayUsedWithMultipleTypes()) { GenerateWarning("%s: bound element array buffer previously used with a type other than " "%s, this will affect performance.", info, WebGLContext::EnumName(type)); } MOZ_ASSERT(gl->IsCurrent()); if (mBoundDrawFramebuffer) { if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(info)) return false; } else { ClearBackbufferIfNeeded(); } if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) { return false; } return true; }
int CNscContext::yylex (YYSTYPE* yylval) { // // Initialize lvalue // *yylval = NULL; // // If we have no stream, return nothing // if (m_pStreamTop == NULL) return EOF; // // If we need to read a line // try_again:; if (m_pStreamTop ->pszNextTokenPos == NULL || *m_pStreamTop ->pszNextTokenPos == 0) { read_another_line:; if (!ReadNextLine (false)) return EOF; } // // Skip the white space // char c; for (;;) { c = *m_pStreamTop ->pszNextTokenPos; if (c == 0) goto read_another_line; else if (c <= ' ' || c > 126) m_pStreamTop ->pszNextTokenPos++; else break; } // // If we have an identifier // if (isalpha (c) || c == '_') { char *pszStart = m_pStreamTop ->pszNextTokenPos; m_pStreamTop ->pszNextTokenPos++; for (;;) { c = *m_pStreamTop ->pszNextTokenPos; if (isalnum (c) || c == '_') m_pStreamTop ->pszNextTokenPos++; else break; } // // Get the hash value for the ID // int nCount = (int) (m_pStreamTop ->pszNextTokenPos - pszStart); UINT32 ulHash = CNscSymbolTable::GetHash (pszStart, nCount); // // See if it is a reserved word // NscSymbol *pSymbol = g_sNscReservedWords .Find (pszStart, nCount, ulHash); // // If so, return that word // if (pSymbol != NULL) { assert (pSymbol ->nSymType == NscSymType_Token); if (pSymbol ->nToken == ENGINE_TYPE) { CNscPStackEntry *pEntry = GetPStackEntry (__FILE__, __LINE__); pEntry ->SetType ((NscType) ( NscType_Engine_0 + pSymbol ->nEngineObject)); *yylval = pEntry; return pSymbol ->nToken; } else return pSymbol ->nToken; } else { CNscPStackEntry *pEntry = GetPStackEntry (__FILE__, __LINE__); pEntry ->SetIdentifier (pszStart, nCount); *yylval = pEntry; return IDENTIFIER; } } // // If we have a number. // // The bioware compiler doesn't like a number starting // with a '.' // else if (isdigit (c)) { // // If this is a hex value // if (c == '0' && (m_pStreamTop ->pszNextTokenPos [1] == 'x' || m_pStreamTop ->pszNextTokenPos [1] == 'X')) { // // Parse the number // m_pStreamTop ->pszNextTokenPos += 2; int nValue = 0; for (;;) { c = *m_pStreamTop ->pszNextTokenPos; if (isdigit (c)) { nValue = nValue * 16 + (c - '0'); m_pStreamTop ->pszNextTokenPos++; } else if (c >= 'A' && c <= 'F') { nValue = nValue * 16 + (c - 'A' + 10); m_pStreamTop ->pszNextTokenPos++; } else if (c >= 'a' && c <= 'f') { nValue = nValue * 16 + (c - 'a' + 10); m_pStreamTop ->pszNextTokenPos++; } else break; } // // Return results // CNscPStackEntry *pEntry = GetPStackEntry (__FILE__, __LINE__); pEntry ->SetType (NscType_Integer); pEntry ->PushConstantInteger (nValue); *yylval = pEntry; return INTEGER_CONST; } // // Otherwise, treat as a normal number // else { // // Parse the number // bool fHasDecimal = false; char *pszStart = m_pStreamTop ->pszNextTokenPos; for (;;) { c = *m_pStreamTop ->pszNextTokenPos; if (isdigit (c)) m_pStreamTop ->pszNextTokenPos++; else if (c == '.' && !fHasDecimal) { fHasDecimal = true; m_pStreamTop ->pszNextTokenPos++; } else break; } // // Test for 'F' extension // int nCharacter = (int) (m_pStreamTop ->pszNextTokenPos - pszStart); if (c == 'f' || c == 'F') { fHasDecimal = true; m_pStreamTop ->pszNextTokenPos++; } // // Convert the value // char *psz = (char *) alloca (nCharacter + 1); memcpy (psz, pszStart, nCharacter); psz [nCharacter] = 0; CNscPStackEntry *pEntry = GetPStackEntry (__FILE__, __LINE__); *yylval = pEntry; if (fHasDecimal) { pEntry ->SetType (NscType_Float); pEntry ->PushConstantFloat ((float) atof (psz)); return FLOAT_CONST; } else { pEntry ->SetType (NscType_Integer); pEntry ->PushConstantInteger (atol (psz)); return INTEGER_CONST; } } } // // Otherwise, we have a symbol (hopefully) // else { m_pStreamTop ->pszNextTokenPos++; switch (c) { case '/': c = *m_pStreamTop ->pszNextTokenPos; if (c == '*') { m_pStreamTop ->pszNextTokenPos++; for (;;) { if (m_pStreamTop ->pszNextTokenPos [0] == '*' && m_pStreamTop ->pszNextTokenPos [1] == '/') { m_pStreamTop ->pszNextTokenPos += 2; goto try_again; } else if (m_pStreamTop ->pszNextTokenPos [0] == 0) { if (!ReadNextLine (true)) { //GenerateError ("End of file reached while processing comment"); if (!IsPhase2 ()) GenerateWarning ("End of file reached while processing comment"); return EOF; } } else m_pStreamTop ->pszNextTokenPos++; } } else if (c == '/') { m_pStreamTop ->pszNextTokenPos++; goto read_another_line; } else if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return DIVEQ; } else { return '/'; } break; case '{': case '}': case '[': case ']': case '(': case ')': case ';': case ':': case '?': case ',': case '~': case '.': return c; case '+': c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return ADDEQ; } else if (c == '+') { m_pStreamTop ->pszNextTokenPos++; return PLUSPLUS; } else return '+'; break; case '-': c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return SUBEQ; } else if (c == '-') { m_pStreamTop ->pszNextTokenPos++; return MINUSMINUS; } else return '-'; break; case '*': c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return MULEQ; } else return '*'; break; case '%': c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return MODEQ; } else return '%'; break; case '^': c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return XOREQ; } else return '^'; break; case '&': c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return ANDEQ; } else if (c == '&') { m_pStreamTop ->pszNextTokenPos++; return ANDAND; } else return '&'; break; case '|': c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return OREQ; } else if (c == '|') { m_pStreamTop ->pszNextTokenPos++; return OROR; } else return '|'; break; case '!': c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return NOTEQ; } else return '!'; break; case '=': c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return EQ; } else return '='; break; case '<': c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return LTEQ; } else if (c == '<') { m_pStreamTop ->pszNextTokenPos++; c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return SLEQ; } else return SL; } else return '<'; break; case '>': c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return GTEQ; } else if (c == '>') { m_pStreamTop ->pszNextTokenPos++; c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return SREQ; } else if (c == '>') { m_pStreamTop ->pszNextTokenPos++; c = *m_pStreamTop ->pszNextTokenPos; if (c == '=') { m_pStreamTop ->pszNextTokenPos++; return USREQ; } else return USR; } else return SR; } else return '>'; break; case '"': { char *pszStart = m_pStreamTop ->pszNextTokenPos; char *pszOut = pszStart; for (;;) { c = *m_pStreamTop ->pszNextTokenPos++; if (c == '"') { CNscPStackEntry *pEntry = GetPStackEntry (__FILE__, __LINE__); pEntry ->SetType (NscType_String); pEntry ->PushConstantString (pszStart, (int) (pszOut - pszStart)); *yylval = pEntry; return STRING_CONST; } else if (c == '\\') { c = *m_pStreamTop ->pszNextTokenPos; if (c == 'n') { *pszOut++ = '\n'; m_pStreamTop ->pszNextTokenPos++; } else ; } else if (c == 0) { CNscPStackEntry *pEntry = GetPStackEntry (__FILE__, __LINE__); pEntry ->SetType (NscType_String); pEntry ->PushConstantString (pszStart, (int) (pszOut - pszStart)); *yylval = pEntry; GenerateError ("Unterminated string"); return STRING_CONST; } else *pszOut++ = c; } } break; default: if (!IsPhase2 ()) { GenerateWarning ("Invalid character '%c' (0x%02X) " "found in source, ignored", c, c); } goto try_again; } } }
bool WebGLContext::InitWebGL2() { MOZ_ASSERT(IsWebGL2(), "WebGLContext is not a WebGL 2 context!"); // check OpenGL features if (!gl->IsSupported(gl::GLFeature::occlusion_query) && !gl->IsSupported(gl::GLFeature::occlusion_query_boolean)) { // On desktop, we fake occlusion_query_boolean with occlusion_query if // necessary. (See WebGL2ContextQueries.cpp) GenerateWarning("WebGL 2 unavailable. Requires occlusion queries."); return false; } std::vector<gl::GLFeature> missingList; for (size_t i = 0; i < ArrayLength(kRequiredFeatures); i++) { if (!gl->IsSupported(kRequiredFeatures[i])) missingList.push_back(kRequiredFeatures[i]); } #ifdef XP_MACOSX // On OSX, GL core profile is used. This requires texture swizzle // support to emulate legacy texture formats: ALPHA, LUMINANCE, // and LUMINANCE_ALPHA. if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) missingList.push_back(gl::GLFeature::texture_swizzle); #endif if (missingList.size()) { nsAutoCString exts; for (auto itr = missingList.begin(); itr != missingList.end(); ++itr) { exts.AppendLiteral("\n "); exts.Append(gl::GLContext::GetFeatureName(*itr)); } GenerateWarning("WebGL 2 unavailable. The following required features are" " unavailible: %s", exts.BeginReading()); return false; } // we initialise WebGL 2 related stuff. gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &mGLMaxTransformFeedbackSeparateAttribs); gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS, &mGLMaxUniformBufferBindings); mBoundTransformFeedbackBuffers.SetLength(mGLMaxTransformFeedbackSeparateAttribs); mBoundUniformBuffers.SetLength(mGLMaxUniformBufferBindings); mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0); mBoundTransformFeedback = mDefaultTransformFeedback; if (!gl->IsGLES()) { // Desktop OpenGL requires the following to be enabled in order to // support sRGB operations on framebuffers. gl->MakeCurrent(); gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT); } return true; }