void WebGL2Context::UniformMatrix2x3fv_base(WebGLUniformLocation* loc, bool transpose, size_t arrayLength, const GLfloat* data) { GLuint rawLoc; GLsizei numElementsToUpload; if (!ValidateUniformMatrixArraySetter(loc, 2, 3, LOCAL_GL_FLOAT, arrayLength, transpose, "uniformMatrix2x3fv", &rawLoc, &numElementsToUpload)) { return; } MakeContextCurrent(); gl->fUniformMatrix2x3fv(rawLoc, numElementsToUpload, transpose, data); }
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::BufferSubDataT(GLenum target, WebGLsizeiptr byteOffset, const BufferT& data) { if (IsContextLost()) return; if (!ValidateBufferTarget(target, "bufferSubData")) return; WebGLRefPtr<WebGLBuffer>& bufferSlot = GetBufferSlotByTarget(target); if (byteOffset < 0) return ErrorInvalidValue("bufferSubData: negative offset"); WebGLBuffer* boundBuffer = bufferSlot.get(); if (!boundBuffer) return ErrorInvalidOperation("bufferData: no buffer bound!"); data.ComputeLengthAndData(); CheckedInt<WebGLsizeiptr> checked_neededByteLength = CheckedInt<WebGLsizeiptr>(byteOffset) + data.Length(); if (!checked_neededByteLength.isValid()) { ErrorInvalidValue("bufferSubData: Integer overflow computing the needed" " byte length."); return; } if (checked_neededByteLength.value() > boundBuffer->ByteLength()) { ErrorInvalidValue("bufferSubData: Not enough data. Operation requires" " %d bytes, but buffer only has %d bytes.", checked_neededByteLength.value(), boundBuffer->ByteLength()); return; } boundBuffer->ElementArrayCacheBufferSubData(byteOffset, data.Data(), data.Length()); MakeContextCurrent(); gl->fBufferSubData(target, byteOffset, data.Length(), data.Data()); }
bool WebGL2Context::IsSampler(WebGLSampler* sampler) { if (IsContextLost()) return false; if (!sampler) return false; if (!ValidateObjectAllowDeleted("isSampler", sampler)) return false; if (sampler->IsDeleted()) return false; MakeContextCurrent(); return gl->fIsSampler(sampler->mGLName); }
bool WebGLContext::IsVertexArray(WebGLVertexArray* array) { if (IsContextLost()) return false; if (!array) return false; if (!ValidateObjectAllowDeleted("isVertexArray", array)) return false; if (array->IsDeleted()) return false; MakeContextCurrent(); return array->IsVertexArray(); }
void WebGL2Context::ResumeTransformFeedback() { if (IsContextLost()) return; WebGLTransformFeedback* tf = mBoundTransformFeedback; MOZ_ASSERT(tf); if (!tf) return; if (!tf->mIsActive || !tf->mIsPaused) return ErrorInvalidOperation("resumeTransformFeedback: transform feedback is not active or is not paused"); MakeContextCurrent(); gl->fResumeTransformFeedback(); tf->mIsPaused = false; }
void WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex) { if (IsContextLost()) return; if (!ValidateObjectAllowDeletedOrNull("bindTexture", newTex)) return; // Need to check rawTarget first before comparing against newTex->Target() as // newTex->Target() returns a TexTarget, which will assert on invalid value. WebGLRefPtr<WebGLTexture>* currentTexPtr = nullptr; switch (rawTarget) { case LOCAL_GL_TEXTURE_2D: currentTexPtr = &mBound2DTextures[mActiveTexture]; break; case LOCAL_GL_TEXTURE_CUBE_MAP: currentTexPtr = &mBoundCubeMapTextures[mActiveTexture]; break; case LOCAL_GL_TEXTURE_3D: if (!IsWebGL2()) return ErrorInvalidEnum("bindTexture: target TEXTURE_3D is only available in WebGL version 2.0 or newer"); currentTexPtr = &mBound3DTextures[mActiveTexture]; break; default: return ErrorInvalidEnumInfo("bindTexture: target", rawTarget); } const TexTarget texTarget(rawTarget); MakeContextCurrent(); if (newTex && !newTex->BindTexture(texTarget)) return; if (!newTex) { gl->fBindTexture(texTarget.get(), 0); } *currentTexPtr = newTex; }
void WebGL2Context::ReadBuffer(GLenum mode) { if (IsContextLost()) return; const bool isColorAttachment = (mode >= LOCAL_GL_COLOR_ATTACHMENT0 && mode <= LastColorAttachmentEnum()); if (mode != LOCAL_GL_NONE && mode != LOCAL_GL_BACK && !isColorAttachment) { ErrorInvalidEnum("readBuffer: `mode` must be one of NONE, BACK, or " "COLOR_ATTACHMENTi. Was %s", EnumName(mode)); return; } if (mBoundReadFramebuffer) { if (mode != LOCAL_GL_NONE && !isColorAttachment) { ErrorInvalidOperation("readBuffer: If READ_FRAMEBUFFER is non-null, `mode` " "must be COLOR_ATTACHMENTi or NONE. Was %s", EnumName(mode)); return; } MakeContextCurrent(); mBoundReadFramebuffer->SetReadBufferMode(mode); gl->fReadBuffer(mode); return; } // Operating on the default framebuffer. if (mode != LOCAL_GL_NONE && mode != LOCAL_GL_BACK) { ErrorInvalidOperation("readBuffer: If READ_FRAMEBUFFER is null, `mode`" " must be BACK or NONE. Was %s", EnumName(mode)); return; } gl->Screen()->SetReadBuffer(mode); }
Window::Window(const int2 & dimensions, const char * title) { if(glfwInit() == GL_FALSE) throw std::runtime_error("glfwInit() failed."); window = glfwCreateWindow(dimensions.x, dimensions.y, title, nullptr, nullptr); if(window == nullptr) throw std::runtime_error("glfwCreateWindow(...) failed."); glfwSetWindowUserPointer(window, this); glfwSetKeyCallback(window, OnKey); const uint32_t compressedFont[] = { 0,0x18,0,0,0x660000,0xc000018,0,0,0x36661800,0xc1c003e,0xc30,0x40000000,0x36663c00,0xc360063,0x1818,0x60000000,0x7f243c00,0x6364343,0x1866300c,0x30000000,0x36003c00,0x1c6303,0x183c300c, 0x18000000,0x36001800,0x6e303e,0x7eff300c,0xc007f00,0x36001800,0x3b1860,0x183c300c,0x6000000,0x7f000000,0x330c61,0x1866300c,0x3000018,0x36001800,0x336663,0x1818,0x1180018,0x36001800, 0x6e633e,0xc30,0x180018,0,0x18,0,0xc,0,0x18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x3e3e183e,0x7f1c7f30,0x3e3e,0x3e060060,0x63631c63,0x63060338,0x18186363,0x630c0030,0x60601e73,0x6003033c,0x18186363, 0x63180018,0x6030187b,0x30030336,0x6363,0x30307e0c,0x3c18186f,0x183f3f33,0x7e3e,0x18600006,0x600c1867,0xc63607f,0x6063,0x1830000c,0x60061863,0xc636030,0x18186063,0x187e18,0x63631863, 0xc636330,0x18183063,0x180c0030,0x3e7f7e3e,0xc3e3e78,0xc001e3e,0x18060060,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x3c3f083e,0x3c7f7f1f,0x67783c63,0x1c63630f,0x66661c63,0x66666636, 0x66301863,0x36677706,0x43663663,0x43464666,0x36301863,0x636f7f06,0x366637b,0x3161666,0x36301863,0x637f7f06,0x33e637b,0x31e1e66,0x1e30187f,0x637b6b06,0x3667f7b,0x7b161666,0x36301863, 0x63736306,0x4366633b,0x63064666,0x36331863,0x63636346,0x66666303,0x66066636,0x66331863,0x36636366,0x3c3f633e,0x5c0f7f1f,0x671e3c63,0x1c63637f,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x80000,0,0,0, 0x1c0000,0x3e3f3e3f,0x6363637e,0x3c7f6663,0x363c01,0x63666366,0x6363637e,0xc636663,0x633003,0x63666366,0x6363635a,0xc316636,0x3007,0x6666366,0x63636318,0xc18661c,0x300e,0x1c3e633e, 0x6b636318,0xc0c3c1c,0x301c,0x30366b06,0x6b636318,0xc06181c,0x3038,0x63667b06,0x7f366318,0xc431836,0x3070,0x63663e06,0x3e1c6318,0xc631863,0x3060,0x3e67300f,0x36083e3c,0x3c7f3c63,0x3c40, 0x7000,0,0,0,0,0,0,0xff000000,0,0,0,0,0xc,0,0,0,0xc,0,0,0,0x70018,0x1c0038,0x7601807,0x1c,0x60000,0x360030,0x6601806,0x18,0x60000,0x260030,0x6000006,0x18,0x3e1e1e00,0x6e063e3c,0x66701c36, 0x3e3b3718,0x63363000,0x330f6336,0x3660186e,0x63667f18,0x3663e00,0x33067f33,0x1e601866,0x63666b18,0x3663300,0x33060333,0x36601866,0x63666b18,0x63663300,0x3e066333,0x66601866,0x63666b18, 0x3e3e6e00,0x300f3e6e,0x67663c67,0x3e66633c,0,0x33000000,0x660000,0,0,0x1e000000,0x3c0000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x8,0x70000000,0x6e0e18,0,0xc,0x18000000,0x3b1818,0,0xc,0x18000000, 0x1818,0x3e3b6e3b,0x6366333f,0x187f6363,0x1818,0x636e3366,0x6366330c,0xe336336,0x7000,0xe663366,0x6b66330c,0x1818631c,0x1818,0x38063366,0x6b66330c,0x180c631c,0x1818,0x63063e3e,0x7f3c336c, 0x18667e36,0x1818,0x3e0f3006,0x36186e38,0x707f6063,0xe18,0x3006,0,0x3000,0,0x780f,0,0x1f00 }; uint8_t fontPixels[128*128] = {}; auto out = fontPixels; for(auto num : compressedFont) { for(int i=0; i<32; ++i) { *out++ = num & (1 << i) ? 255 : 0; } } MakeContextCurrent(); glGenTextures(1, &fontTexture); glBindTexture(GL_TEXTURE_2D, fontTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, fontPixels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glBindTexture(GL_TEXTURE_2D, 0); }
void WebGLContext::Enable(GLenum cap) { if (IsContextLost()) return; if (!ValidateCapabilityEnum(cap, "enable")) return; realGLboolean* trackingSlot = GetStateTrackingSlot(cap); if (trackingSlot) { *trackingSlot = 1; } MakeContextCurrent(); gl->fEnable(cap); }
void WebGL2Context::EndQuery(GLenum target) { if (IsContextLost()) return; if (!ValidateQueryTarget(target, "endQuery")) return; WebGLRefPtr<WebGLQuery>& querySlot = GetQuerySlotByTarget(target); WebGLQuery* activeQuery = querySlot.get(); if (!activeQuery || target != activeQuery->mType) { /* From GLES's EXT_occlusion_query_boolean: * marks the end of the sequence of commands to be tracked for the * query type given by <target>. The active query object for * <target> is updated to indicate that query results are not * available, and the active query object name for <target> is reset * to zero. When the commands issued prior to EndQueryEXT have * completed and a final query result is available, the query object * active when EndQueryEXT is called is updated by the GL. The query * object is updated to indicate that the query results are * available and to contain the query result. If the active query * object name for <target> is zero when EndQueryEXT is called, the * error INVALID_OPERATION is generated. */ ErrorInvalidOperation("endQuery: There is no active query of type %s.", GetQueryTargetEnumString(target)); return; } MakeContextCurrent(); if (target == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) { gl->fEndQuery(LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); } else { gl->fEndQuery(SimulateOcclusionQueryTarget(gl, target)); } UpdateBoundQuery(target, nullptr); NS_DispatchToCurrentThread(new WebGLQuery::AvailableRunnable(activeQuery)); }
void WebGLContext::DisableVertexAttribArray(GLuint index) { if (IsContextLost()) return; if (!ValidateAttribIndex(index, "disableVertexAttribArray")) return; MakeContextCurrent(); InvalidateBufferFetching(); if (index || !gl->IsCompatibilityProfile()) { gl->fDisableVertexAttribArray(index); } MOZ_ASSERT(mBoundVertexArray); mBoundVertexArray->mAttribs[index].mEnabled = false; }
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::PauseTransformFeedback() { if (IsContextLost()) return; WebGLTransformFeedback* tf = mBoundTransformFeedback; MOZ_ASSERT(tf); if (!tf) return; if (!tf->mIsActive || tf->mIsPaused) { return ErrorInvalidOperation("%s: transform feedback is not active or is paused", "pauseTransformFeedback"); } MakeContextCurrent(); gl->fPauseTransformFeedback(); tf->mIsPaused = true; }
void WebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor) { if (IsContextLost()) return; if (!ValidateAttribIndex(index, "vertexAttribDivisor")) return; MOZ_ASSERT(mBoundVertexArray); WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index]; vd.mDivisor = divisor; InvalidateBufferFetching(); MakeContextCurrent(); gl->fVertexAttribDivisor(index, divisor); }
/* This doesn't belong here. It's part of state querying */ void WebGL2Context::GetIndexedParameter(GLenum target, GLuint index, dom::Nullable<dom::OwningWebGLBufferOrLongLong>& retval) { retval.SetNull(); if (IsContextLost()) return; GLint64 data = 0; MakeContextCurrent(); switch (target) { case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING: if (index >= mGLMaxTransformFeedbackSeparateAttribs) return ErrorInvalidValue("getIndexedParameter: index should be less than " "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS"); retval.SetValue().SetAsWebGLBuffer() = mBoundTransformFeedbackBuffers[index].get(); return; case LOCAL_GL_UNIFORM_BUFFER_BINDING: if (index >= mGLMaxUniformBufferBindings) return ErrorInvalidValue("getIndexedParameter: index should be than " "MAX_UNIFORM_BUFFER_BINDINGS"); retval.SetValue().SetAsWebGLBuffer() = mBoundUniformBuffers[index].get(); return; case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_START: case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_SIZE: case LOCAL_GL_UNIFORM_BUFFER_START: case LOCAL_GL_UNIFORM_BUFFER_SIZE: gl->fGetInteger64i_v(target, index, &data); retval.SetValue().SetAsLongLong() = data; return; } ErrorInvalidEnumInfo("getIndexedParameter: target", target); }
void WebGL2Context::EndTransformFeedback() { if (IsContextLost()) return; WebGLTransformFeedback* tf = mBoundTransformFeedback; MOZ_ASSERT(tf); if (!tf) return; if (!tf->mIsActive) return ErrorInvalidOperation("%s: transform feedback in not active", "endTransformFeedback"); MakeContextCurrent(); gl->fEndTransformFeedback(); tf->mIsActive = false; tf->mIsPaused = false; }
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::DrawElements(GLenum mode, GLsizei count, GLenum type, WebGLintptr byteOffset) { const char funcName[] = "drawElements"; if (IsContextLost()) return; if (!ValidateDrawModeEnum(mode, funcName)) return; MakeContextCurrent(); bool error; ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error); if (error) return; GLuint upperBound = 0; if (!DrawElements_check(count, type, byteOffset, 1, funcName, &upperBound)) return; RunContextLossTimer(); { ScopedMaskWorkaround autoMask(*this); if (gl->IsSupported(gl::GLFeature::draw_range_elements)) { gl->fDrawRangeElements(mode, 0, upperBound, count, type, reinterpret_cast<GLvoid*>(byteOffset)); } else { gl->fDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset)); } } Draw_cleanup(funcName); }
void WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount, GLsizei instanceCount) { const char funcName[] = "drawArraysInstanced"; if (IsContextLost()) return; MakeContextCurrent(); bool error = false; ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error); if (error) return; if (!DrawArrays_check(funcName, mode, first, vertCount, instanceCount)) return; if (!DrawInstanced_check(funcName)) return; const ScopedDrawHelper scopedHelper(this, funcName, first, vertCount, instanceCount, &error); if (error) return; const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount, instanceCount, &error); if (error) return; { ScopedDrawCallWrapper wrapper(*this); gl->fDrawArraysInstanced(mode, first, vertCount, instanceCount); } Draw_cleanup(funcName); scopedTF.Advance(); }
void WebGL2Context::BindTransformFeedback(GLenum target, WebGLTransformFeedback* tf) { const char funcName[] = "bindTransformFeedback"; if (IsContextLost()) return; if (target != LOCAL_GL_TRANSFORM_FEEDBACK) return ErrorInvalidEnum("%s: `target` must be TRANSFORM_FEEDBACK.", funcName); if (tf && !ValidateObject(funcName, *tf)) return; if (mBoundTransformFeedback->mIsActive && !mBoundTransformFeedback->mIsPaused) { ErrorInvalidOperation("%s: Currently bound transform feedback is active and not" " paused.", funcName); return; } //// if (mBoundTransformFeedback) { mBoundTransformFeedback->AddBufferBindCounts(-1); } mBoundTransformFeedback = (tf ? tf : mDefaultTransformFeedback); MakeContextCurrent(); gl->fBindTransformFeedback(target, mBoundTransformFeedback->mGLName); if (mBoundTransformFeedback) { mBoundTransformFeedback->AddBufferBindCounts(+1); } }
void WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, WebGLintptr byteOffset, GLsizei primcount) { const char funcName[] = "drawElementsInstanced"; if (IsContextLost()) return; if (!ValidateDrawModeEnum(mode, funcName)) return; MakeContextCurrent(); bool error; ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error); if (error) return; GLuint upperBound = 0; if (!DrawElements_check(count, type, byteOffset, primcount, funcName, &upperBound)) return; if (!DrawInstanced_check(funcName)) return; RunContextLossTimer(); { ScopedMaskWorkaround autoMask(*this); gl->fDrawElementsInstanced(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset), primcount); } Draw_cleanup(funcName); }
void WebGLContext::ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { if (IsContextLost()) return; MakeContextCurrent(); const bool supportsFloatColorBuffers = (IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) || IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)); if (!supportsFloatColorBuffers) { r = GLClampFloat(r); g = GLClampFloat(g); b = GLClampFloat(b); a = GLClampFloat(a); } gl->fClearColor(r, g, b, a); mColorClearValue[0] = r; mColorClearValue[1] = g; mColorClearValue[2] = b; mColorClearValue[3] = a; }
void WebGL2Context::BeginTransformFeedback(GLenum primitiveMode) { if (IsContextLost()) return; WebGLTransformFeedback* tf = mBoundTransformFeedback; MOZ_ASSERT(tf); if (!tf) return; if (tf->mIsActive) return ErrorInvalidOperation("beginTransformFeedback: transform feedback is active"); const GLenum mode = tf->mMode; if (mode != LOCAL_GL_POINTS && mode != LOCAL_GL_LINES && mode != LOCAL_GL_TRIANGLES) return ErrorInvalidEnum("beginTransformFeedback: primitive must be one of POINTS, LINES, or TRIANGLES"); // TODO: // GL_INVALID_OPERATION is generated by glBeginTransformFeedback // if any binding point used in transform feedback mode does not // have a buffer object bound. In interleaved mode, only the first // buffer object binding point is ever written to. // GL_INVALID_OPERATION is generated by glBeginTransformFeedback // if no binding points would be used, either because no program // object is active of because the active program object has // specified no varying variables to record. if (!mCurrentProgram) return ErrorInvalidOperation("beginTransformFeedback: no program is active"); MakeContextCurrent(); gl->fBeginTransformFeedback(primitiveMode); tf->mIsActive = true; tf->mIsPaused = false; }
JS::Value WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname, ErrorResult& rv) { const char funcName[] = "getVertexAttrib"; if (IsContextLost()) return JS::NullValue(); if (!ValidateAttribIndex(index, funcName)) return JS::NullValue(); MOZ_ASSERT(mBoundVertexArray); MakeContextCurrent(); switch (pname) { case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribs[index].mBuf.get(), rv); case LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE: return JS::Int32Value(mBoundVertexArray->mAttribs[index].Stride()); case LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE: return JS::Int32Value(mBoundVertexArray->mAttribs[index].Size()); case LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE: return JS::Int32Value(mBoundVertexArray->mAttribs[index].Type()); case LOCAL_GL_VERTEX_ATTRIB_ARRAY_INTEGER: if (IsWebGL2()) return JS::BooleanValue(mBoundVertexArray->mAttribs[index].IntegerFunc()); break; case LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR: if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays)) { return JS::Int32Value(mBoundVertexArray->mAttribs[index].mDivisor); } break; case LOCAL_GL_CURRENT_VERTEX_ATTRIB: { JS::RootedObject obj(cx); switch (mGenericVertexAttribTypes[index]) { case LOCAL_GL_FLOAT: obj = GetVertexAttribFloat32Array(cx, index); break; case LOCAL_GL_INT: obj = GetVertexAttribInt32Array(cx, index); break; case LOCAL_GL_UNSIGNED_INT: obj = GetVertexAttribUint32Array(cx, index); break; } if (!obj) rv.Throw(NS_ERROR_OUT_OF_MEMORY); return JS::ObjectOrNullValue(obj); } case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED: return JS::BooleanValue(mBoundVertexArray->mAttribs[index].mEnabled); case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED: return JS::BooleanValue(mBoundVertexArray->mAttribs[index].Normalized()); default: break; } ErrorInvalidEnumInfo("getVertexAttrib: parameter", pname); return JS::NullValue(); }
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 WebGL2Context::ValidateClearBuffer(const char* funcName, GLenum buffer, GLint drawBuffer, size_t availElemCount, GLuint elemOffset, GLenum funcType) { if (elemOffset > availElemCount) { ErrorInvalidValue("%s: Offset too big for list.", funcName); return false; } availElemCount -= elemOffset; //// size_t requiredElements; GLint maxDrawBuffer; switch (buffer) { case LOCAL_GL_COLOR: requiredElements = 4; maxDrawBuffer = mGLMaxDrawBuffers - 1; break; case LOCAL_GL_DEPTH: case LOCAL_GL_STENCIL: requiredElements = 1; maxDrawBuffer = 0; break; case LOCAL_GL_DEPTH_STENCIL: requiredElements = 2; maxDrawBuffer = 0; break; default: ErrorInvalidEnumInfo(funcName, buffer); return false; } if (drawBuffer < 0 || drawBuffer > maxDrawBuffer) { ErrorInvalidValue("%s: Invalid drawbuffer %d. This buffer only supports" " `drawbuffer` values between 0 and %u.", funcName, drawBuffer, maxDrawBuffer); return false; } if (availElemCount < requiredElements) { ErrorInvalidValue("%s: Not enough elements. Require %u. Given %u.", funcName, requiredElements, availElemCount); return false; } //// MakeContextCurrent(); const auto& fb = mBoundDrawFramebuffer; if (fb) { if (!fb->ValidateAndInitAttachments(funcName)) return false; if (!fb->ValidateClearBufferType(funcName, buffer, drawBuffer, funcType)) return false; } else if (buffer == LOCAL_GL_COLOR) { if (drawBuffer != 0) return true; if (mDefaultFB_DrawBuffer0 == LOCAL_GL_NONE) return true; if (funcType != LOCAL_GL_FLOAT) { ErrorInvalidOperation("%s: For default framebuffer, COLOR is always of type" " FLOAT.", funcName); return false; } } return true; }
_X_EXPORT Bool glXMakeCurrent(Display * dpy, GLXDrawable draw, GLXContext gc) { return MakeContextCurrent(dpy, draw, draw, gc); }
JS::Value WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) { 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 (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) { return JS::Int32Value(mGLMaxColorAttachments); } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) { return JS::Int32Value(mGLMaxDrawBuffers); } else if (pname >= LOCAL_GL_DRAW_BUFFER0 && pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers)) { 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 (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 (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) { if (pname == LOCAL_GL_TIMESTAMP_EXT) { GLuint64 iv = 0; gl->fGetInteger64v(pname, (GLint64*) &iv); // 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("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 (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: { if (mBoundReadFramebuffer) { FBStatus status = mBoundReadFramebuffer->CheckFramebufferStatus(); if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { ErrorInvalidOperation("getParameter: Read framebuffer must be" " complete before querying" " IMPLEMENTATION_COLOR_READ_TYPE."); 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: { if (mBoundReadFramebuffer) { FBStatus status = mBoundReadFramebuffer->CheckFramebufferStatus(); if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { ErrorInvalidOperation("getParameter: Read framebuffer must be" " complete before querying" " IMPLEMENTATION_COLOR_READ_FORMAT."); return JS::NullValue(); } } GLint i = 0; if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) { gl->fGetIntegerv(pname, &i); } else { 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_BITS: { GLint stencilBits = 0; GetStencilBits(&stencilBits); return JS::Int32Value(stencilBits); } 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: case LOCAL_GL_RED_BITS: case LOCAL_GL_GREEN_BITS: case LOCAL_GL_BLUE_BITS: case LOCAL_GL_DEPTH_BITS: { GLint i = 0; gl->fGetIntegerv(pname, &i); return JS::Int32Value(i); } case LOCAL_GL_ALPHA_BITS: { GLint i = 0; if (!mNeedsFakeNoAlpha) { gl->fGetIntegerv(pname, &i); } return JS::Int32Value(i); } case LOCAL_GL_MAX_TEXTURE_SIZE: return JS::Int32Value(mGLMaxTextureSize); case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE: return JS::Int32Value(mGLMaxCubeMapTextureSize); case LOCAL_GL_MAX_RENDERBUFFER_SIZE: return JS::Int32Value(mGLMaxRenderbufferSize); 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_NUM_COMPRESSED_TEXTURE_FORMATS: return JS::Int32Value(0); 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_DEPTH_CLEAR_VALUE: case LOCAL_GL_LINE_WIDTH: 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(mPixelStoreFlipY); case UNPACK_PREMULTIPLY_ALPHA_WEBGL: return JS::BooleanValue(mPixelStorePremultiplyAlpha); // uint, WebGL-specific case UNPACK_COLORSPACE_CONVERSION_WEBGL: return JS::NumberValue(uint32_t(mPixelStoreColorspaceConversion)); //////////////////////////////// // Complex values // 2 floats case LOCAL_GL_DEPTH_RANGE: case LOCAL_GL_ALIASED_POINT_SIZE_RANGE: case LOCAL_GL_ALIASED_LINE_WIDTH_RANGE: { GLfloat fv[2] = { 0 }; gl->fGetFloatv(pname, 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(); }
void WebGL2Context::GetActiveUniforms(JSContext* cx, const WebGLProgram& program, const dom::Sequence<GLuint>& uniformIndices, GLenum pname, JS::MutableHandleValue retval) { const char funcName[] = "getActiveUniforms"; retval.setNull(); if (IsContextLost()) return; if (!ValidateUniformEnum(this, pname, funcName)) return; if (!ValidateObject("getActiveUniforms: program", program)) return; const auto& numActiveUniforms = program.LinkInfo()->uniforms.size(); for (const auto& curIndex : uniformIndices) { if (curIndex >= numActiveUniforms) { ErrorInvalidValue("%s: Too-large active uniform index queried.", funcName); return; } } const auto& count = uniformIndices.Length(); JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, count)); UniquePtr<GLint[]> samples(new GLint[count]); if (!array || !samples) { ErrorOutOfMemory("%s: Failed to allocate buffers.", funcName); return; } retval.setObject(*array); MakeContextCurrent(); gl->fGetActiveUniformsiv(program.mGLName, count, uniformIndices.Elements(), pname, samples.get()); switch (pname) { case LOCAL_GL_UNIFORM_TYPE: case LOCAL_GL_UNIFORM_SIZE: case LOCAL_GL_UNIFORM_BLOCK_INDEX: case LOCAL_GL_UNIFORM_OFFSET: case LOCAL_GL_UNIFORM_ARRAY_STRIDE: case LOCAL_GL_UNIFORM_MATRIX_STRIDE: for (size_t i = 0; i < count; ++i) { JS::RootedValue value(cx); value = JS::Int32Value(samples[i]); if (!JS_DefineElement(cx, array, i, value, JSPROP_ENUMERATE)) return; } break; case LOCAL_GL_UNIFORM_IS_ROW_MAJOR: for (size_t i = 0; i < count; ++i) { JS::RootedValue value(cx); value = JS::BooleanValue(samples[i]); if (!JS_DefineElement(cx, array, i, value, JSPROP_ENUMERATE)) return; } break; default: MOZ_CRASH("Invalid pname"); } }