void WebGL2Context::GetUniformIndices(WebGLProgram* program, const dom::Sequence<nsString>& uniformNames, dom::Nullable< nsTArray<GLuint> >& retval) { retval.SetNull(); if (IsContextLost()) return; if (!ValidateObject("getUniformIndices: program", program)) return; if (!uniformNames.Length()) return; GLuint progname = program->mGLName; size_t count = uniformNames.Length(); nsTArray<GLuint>& arr = retval.SetValue(); MakeContextCurrent(); for (size_t n = 0; n < count; n++) { NS_LossyConvertUTF16toASCII name(uniformNames[n]); // const GLchar* glname = name.get(); const GLchar* glname = nullptr; name.BeginReading(glname); GLuint index = 0; gl->fGetUniformIndices(progname, 1, &glname, &index); arr.AppendElement(index); } }
void WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments, GLint x, GLint y, GLsizei width, GLsizei height, ErrorResult& rv) { if (IsContextLost()) return; MakeContextCurrent(); if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer")) return; const WebGLFramebuffer* fb; bool isDefaultFB; switch (target) { case LOCAL_GL_FRAMEBUFFER: case LOCAL_GL_DRAW_FRAMEBUFFER: fb = mBoundDrawFramebuffer; isDefaultFB = gl->Screen()->IsDrawFramebufferDefault(); break; case LOCAL_GL_READ_FRAMEBUFFER: fb = mBoundReadFramebuffer; isDefaultFB = gl->Screen()->IsReadFramebufferDefault(); break; default: MOZ_CRASH("Bad target."); } for (size_t i = 0; i < attachments.Length(); i++) { if (!ValidateFramebufferAttachment(fb, attachments[i], "invalidateSubFramebuffer")) { return; } } // InvalidateFramebuffer is a hint to the driver. Should be OK to // skip calls if not supported, for example by OSX 10.9 GL // drivers. static bool invalidateFBSupported = gl->IsSupported(gl::GLFeature::invalidate_framebuffer); if (!invalidateFBSupported) return; if (!fb && !isDefaultFB) { dom::Sequence<GLenum> tmpAttachments; if (!TranslateDefaultAttachments(attachments, &tmpAttachments)) { rv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements(), x, y, width, height); } else { gl->fInvalidateSubFramebuffer(target, attachments.Length(), attachments.Elements(), x, y, width, height); } }
void WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments, GLint x, GLint y, GLsizei width, GLsizei height) { if (IsContextLost()) return; MakeContextCurrent(); if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer")) return; const WebGLFramebuffer* fb; bool isDefaultFB; switch (target) { case LOCAL_GL_FRAMEBUFFER: case LOCAL_GL_DRAW_FRAMEBUFFER: fb = mBoundDrawFramebuffer; isDefaultFB = gl->Screen()->IsDrawFramebufferDefault(); break; case LOCAL_GL_READ_FRAMEBUFFER: fb = mBoundReadFramebuffer; isDefaultFB = gl->Screen()->IsReadFramebufferDefault(); break; default: MOZ_CRASH("Bad target."); } for (size_t i = 0; i < attachments.Length(); i++) { if (!ValidateFramebufferAttachment(fb, attachments[i], "invalidateSubFramebuffer")) { return; } } if (!fb && !isDefaultFB) { dom::Sequence<GLenum> tmpAttachments; TranslateDefaultAttachments(attachments, &tmpAttachments); gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements(), x, y, width, height); } else { gl->fInvalidateSubFramebuffer(target, attachments.Length(), attachments.Elements(), x, y, width, height); } }
// Map attachments intended for the default buffer, to attachments for a non- // default buffer. static bool TranslateDefaultAttachments(const dom::Sequence<GLenum>& in, dom::Sequence<GLenum>* out) { for (size_t i = 0; i < in.Length(); i++) { switch (in[i]) { case LOCAL_GL_COLOR: if (!out->AppendElement(LOCAL_GL_COLOR_ATTACHMENT0, fallible)) { return false; } break; case LOCAL_GL_DEPTH: if (!out->AppendElement(LOCAL_GL_DEPTH_ATTACHMENT, fallible)) { return false; } break; case LOCAL_GL_STENCIL: if (!out->AppendElement(LOCAL_GL_STENCIL_ATTACHMENT, fallible)) { return false; } break; } } return true; }
void WebGL2Context::GetActiveUniforms(WebGLProgram* program, const dom::Sequence<GLuint>& uniformIndices, GLenum pname, dom::Nullable< nsTArray<GLint> >& retval) { retval.SetNull(); if (IsContextLost()) return; if (pname == LOCAL_GL_UNIFORM_NAME_LENGTH) { ErrorInvalidEnumInfo("getActiveUniforms: pname", pname); return; } if (!ValidateObject("getActiveUniforms: program", program)) return; size_t count = uniformIndices.Length(); if (!count) return; GLuint progname = program->mGLName; nsTArray<GLint>& arr = retval.SetValue(); arr.SetLength(count); MakeContextCurrent(); gl->fGetActiveUniformsiv(progname, count, uniformIndices.Elements(), pname, arr.Elements()); }
void WebGL2Context::GetUniformIndices( const WebGLProgram& program, const dom::Sequence<nsString>& uniformNames, dom::Nullable<nsTArray<GLuint> >& retval) { const FuncScope funcScope(*this, "getUniformIndices"); retval.SetNull(); if (IsContextLost()) return; if (!ValidateObject("program", program)) return; if (!uniformNames.Length()) return; program.GetUniformIndices(uniformNames, retval); }
void WebGL2Context::ClearBufferfv(GLenum buffer, GLint drawbuffer, const dom::Sequence<GLfloat>& value) { if (IsContextLost()) { return; } if (!ValidateClearBuffer("clearBufferfv", buffer, drawbuffer, value.Length())) { return; } ClearBufferfv_base(buffer, drawbuffer, value.Elements()); }
void WebGLProgram::TransformFeedbackVaryings(const dom::Sequence<nsString>& varyings, GLenum bufferMode) { const char funcName[] = "transformFeedbackVaryings"; const auto& gl = mContext->gl; gl->MakeCurrent(); switch (bufferMode) { case LOCAL_GL_INTERLEAVED_ATTRIBS: break; case LOCAL_GL_SEPARATE_ATTRIBS: { GLuint maxAttribs = 0; gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &maxAttribs); if (varyings.Length() > maxAttribs) { mContext->ErrorInvalidValue("%s: Length of `varyings` exceeds %s.", funcName, "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS"); return; } } break; default: mContext->ErrorInvalidEnum("%s: Bad `bufferMode`: 0x%04x.", funcName, bufferMode); return; } //// mNextLink_TransformFeedbackVaryings.assign(varyings.Elements(), varyings.Elements() + varyings.Length()); mNextLink_TransformFeedbackBufferMode = bufferMode; }
void WebGLProgram::GetUniformIndices(const dom::Sequence<nsString>& uniformNames, dom::Nullable< nsTArray<GLuint> >& retval) const { const char funcName[] = "getUniformIndices"; if (!IsLinked()) { mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName); return; } size_t count = uniformNames.Length(); nsTArray<GLuint>& arr = retval.SetValue(); gl::GLContext* gl = mContext->GL(); gl->MakeCurrent(); for (size_t i = 0; i < count; i++) { const NS_LossyConvertUTF16toASCII userName(uniformNames[i]); nsDependentCString baseUserName; bool isArray; size_t arrayIndex; if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex)) { arr.AppendElement(LOCAL_GL_INVALID_INDEX); continue; } webgl::UniformInfo* info; if (!LinkInfo()->FindUniform(baseUserName, &info)) { arr.AppendElement(LOCAL_GL_INVALID_INDEX); continue; } nsAutoCString mappedName(info->mActiveInfo->mBaseMappedName); if (isArray) { mappedName.AppendLiteral("["); mappedName.AppendInt(uint32_t(arrayIndex)); mappedName.AppendLiteral("]"); } const GLchar* mappedNameBytes = mappedName.BeginReading(); GLuint index = 0; gl->fGetUniformIndices(mGLName, 1, &mappedNameBytes, &index); arr.AppendElement(index); } }
// Map attachments intended for the default buffer, to attachments for a non- // default buffer. static void TranslateDefaultAttachments(const dom::Sequence<GLenum>& in, dom::Sequence<GLenum>* out) { for (size_t i = 0; i < in.Length(); i++) { switch (in[i]) { case LOCAL_GL_COLOR: out->AppendElement(LOCAL_GL_COLOR_ATTACHMENT0); break; case LOCAL_GL_DEPTH: out->AppendElement(LOCAL_GL_DEPTH_ATTACHMENT); break; case LOCAL_GL_STENCIL: out->AppendElement(LOCAL_GL_STENCIL_ATTACHMENT); break; } } }
void WebGL2Context::GetUniformIndices(WebGLProgram* program, const dom::Sequence<nsString>& uniformNames, dom::Nullable< nsTArray<GLuint> >& retval) { retval.SetNull(); if (IsContextLost()) return; if (!ValidateObject("getUniformIndices: program", program)) return; if (!uniformNames.Length()) return; program->GetUniformIndices(uniformNames, retval); }
void WebGL2Context::SamplerParameterfv(WebGLSampler* sampler, GLenum pname, const dom::Sequence<GLfloat>& param) { if (IsContextLost()) return; if (!sampler || sampler->IsDeleted()) return ErrorInvalidOperation("samplerParameterfv: invalid sampler"); if (param.Length() < 1) return /* TODO(djg): Error message */; /* TODO(djg): All of these calls in ES3 only take 1 param */ if (!ValidateSamplerParameterParams(pname, WebGLIntOrFloat(param[0]), "samplerParameterfv")) return; WebGLContextUnchecked::SamplerParameterfv(sampler, pname, param.Elements()); }
void WebGLContext::DrawBuffers(const dom::Sequence<GLenum>& buffers) { const char funcName[] = "drawBuffers"; if (IsContextLost()) return; if (mBoundDrawFramebuffer) { mBoundDrawFramebuffer->DrawBuffers(funcName, buffers); return; } // GLES 3.0.4 p186: // "If the GL is bound to the default framebuffer, then `n` must be 1 and the // constant must be BACK or NONE. [...] If DrawBuffers is supplied with a // constant other than BACK and NONE, or with a value of `n` other than 1, the // error INVALID_OPERATION is generated." if (buffers.Length() != 1) { ErrorInvalidOperation("%s: For the default framebuffer, `buffers` must have a" " length of 1.", funcName); return; } switch (buffers[0]) { case LOCAL_GL_NONE: case LOCAL_GL_BACK: break; default: ErrorInvalidOperation("%s: For the default framebuffer, `buffers[0]` must be" " BACK or NONE.", funcName); return; } mDefaultFB_DrawBuffer0 = buffers[0]; gl->Screen()->SetDrawBuffer(buffers[0]); }
void WebGLProgram::TransformFeedbackVaryings(const dom::Sequence<nsString>& varyings, GLenum bufferMode) { if (bufferMode != LOCAL_GL_INTERLEAVED_ATTRIBS && bufferMode != LOCAL_GL_SEPARATE_ATTRIBS) { mContext->ErrorInvalidEnum("transformFeedbackVaryings: `bufferMode` %s is " "invalid. Must be one of gl.INTERLEAVED_ATTRIBS or " "gl.SEPARATE_ATTRIBS.", mContext->EnumName(bufferMode)); return; } size_t varyingsCount = varyings.Length(); if (bufferMode == LOCAL_GL_SEPARATE_ATTRIBS && varyingsCount >= mContext->mGLMaxTransformFeedbackSeparateAttribs) { mContext->ErrorInvalidValue("transformFeedbackVaryings: Number of `varyings` exc" "eeds gl.MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS."); return; } std::vector<nsCString> asciiVaryings; for (size_t i = 0; i < varyingsCount; i++) { if (!ValidateGLSLVariableName(varyings[i], mContext, "transformFeedbackVaryings")) return; NS_LossyConvertUTF16toASCII asciiName(varyings[i]); asciiVaryings.push_back(asciiName); } // All validated. Translate the strings and store them until // program linking. mTransformFeedbackBufferMode = bufferMode; mTransformFeedbackVaryings.swap(asciiVaryings); }
void WebGL2Context::TransformFeedbackVaryings(WebGLProgram* program, const dom::Sequence<nsString>& varyings, GLenum bufferMode) { if (IsContextLost()) return; if (!ValidateObject("transformFeedbackVaryings: program", program)) return; GLsizei count = varyings.Length(); GLchar** tmpVaryings = (GLchar**) nsMemory::Alloc(count * sizeof(GLchar*)); for (GLsizei n = 0; n < count; n++) { tmpVaryings[n] = (GLchar*) ToNewCString(varyings[n]); } GLuint progname = program->GLName(); MakeContextCurrent(); gl->fTransformFeedbackVaryings(progname, count, tmpVaryings, bufferMode); NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, tmpVaryings); }
void WebGLContext::DrawBuffers(const dom::Sequence<GLenum>& buffers) { const size_t buffersLength = buffers.Length(); if (buffersLength == 0) { return ErrorInvalidValue("drawBuffers: invalid <buffers> (buffers must not be empty)"); } if (mBoundFramebuffer == 0) { // OK: we are rendering in the default framebuffer /* EXT_draw_buffers : If the GL is bound to the default framebuffer, then <buffersLength> must be 1 and the constant must be BACK or NONE. When draw buffer zero is BACK, color values are written into the sole buffer for single- buffered contexts, or into the back buffer for double-buffered contexts. If DrawBuffersEXT is supplied with a constant other than BACK and NONE, the error INVALID_OPERATION is generated. */ if (buffersLength != 1) { return ErrorInvalidValue("drawBuffers: invalid <buffers> (main framebuffer: buffers.length must be 1)"); } MakeContextCurrent(); if (buffers[0] == LOCAL_GL_NONE) { const GLenum drawBuffersCommand = LOCAL_GL_NONE; gl->fDrawBuffers(1, &drawBuffersCommand); return; } else if (buffers[0] == LOCAL_GL_BACK) { const GLenum drawBuffersCommand = LOCAL_GL_COLOR_ATTACHMENT0; gl->fDrawBuffers(1, &drawBuffersCommand); return; } return ErrorInvalidOperation("drawBuffers: invalid operation (main framebuffer: buffers[0] must be GL_NONE or GL_BACK)"); } // OK: we are rendering in a framebuffer object if (buffersLength > size_t(mGLMaxDrawBuffers)) { /* EXT_draw_buffers : The maximum number of draw buffers is implementation-dependent. The number of draw buffers supported can be queried by calling GetIntegerv with the symbolic constant MAX_DRAW_BUFFERS_EXT. An INVALID_VALUE error is generated if <buffersLength> is greater than MAX_DRAW_BUFFERS_EXT. */ return ErrorInvalidValue("drawBuffers: invalid <buffers> (buffers.length > GL_MAX_DRAW_BUFFERS)"); } for (uint32_t i = 0; i < buffersLength; i++) { /* EXT_draw_buffers : If the GL is bound to a draw framebuffer object, the <i>th buffer listed in <bufs> must be COLOR_ATTACHMENT<i>_EXT or NONE. Specifying a buffer out of order, BACK, or COLOR_ATTACHMENT<m>_EXT where <m> is greater than or equal to the value of MAX_COLOR_ATTACHMENTS_EXT, will generate the error INVALID_OPERATION. */ /* WEBGL_draw_buffers : The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter. */ if (buffers[i] != LOCAL_GL_NONE && buffers[i] != GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + i)) { return ErrorInvalidOperation("drawBuffers: invalid operation (buffers[i] must be GL_NONE or GL_COLOR_ATTACHMENTi)"); } } MakeContextCurrent(); gl->fDrawBuffers(buffersLength, buffers.Elements()); }
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"); } }
void WebGLContext::DrawBuffers(const dom::Sequence<GLenum>& buffers) { const char funcName[] = "drawBuffers"; if (IsContextLost()) return; if (!mBoundDrawFramebuffer) { // GLES 3.0.4 p186: // "If the GL is bound to the default framebuffer, then `n` must be 1 and the // constant must be BACK or NONE. [...] If DrawBuffers is supplied with a // constant other than BACK and NONE, or with a value of `n` other than 1, the // error INVALID_OPERATION is generated." if (buffers.Length() != 1) { ErrorInvalidOperation("%s: For the default framebuffer, `buffers` must have a" " length of 1.", funcName); return; } switch (buffers[0]) { case LOCAL_GL_NONE: case LOCAL_GL_BACK: break; default: ErrorInvalidOperation("%s: For the default framebuffer, `buffers[0]` must be" " BACK or NONE.", funcName); return; } mDefaultFB_DrawBuffer0 = buffers[0]; gl->Screen()->SetDrawBuffer(buffers[0]); return; } // Framebuffer object (not default framebuffer) if (buffers.Length() > mImplMaxDrawBuffers) { // "An INVALID_VALUE error is generated if `n` is greater than MAX_DRAW_BUFFERS." ErrorInvalidValue("%s: `buffers` must have a length <= MAX_DRAW_BUFFERS.", funcName); return; } for (size_t i = 0; i < buffers.Length(); i++) { // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed in // bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of order, // BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to the value of // MAX_COLOR_ATTACHMENTS, will generate the error INVALID_OPERATION. // WEBGL_draw_buffers: // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or // equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter." // This means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't // be larger than MaxColorAttachments. if (buffers[i] != LOCAL_GL_NONE && buffers[i] != LOCAL_GL_COLOR_ATTACHMENT0 + i) { ErrorInvalidOperation("%s: `buffers[i]` must be NONE or COLOR_ATTACHMENTi.", funcName); return; } } MakeContextCurrent(); const GLenum* ptr = nullptr; if (buffers.Length()) { ptr = buffers.Elements(); } gl->fDrawBuffers(buffers.Length(), ptr); const auto end = ptr + buffers.Length(); mBoundDrawFramebuffer->mDrawBuffers.assign(ptr, end); }
void WebGL2Context::VertexAttribI4uiv(GLuint index, const dom::Sequence<GLuint>& v) { VertexAttribI4uiv(index, v.Length(), v.Elements()); }
void WebGL2Context::GetActiveUniforms(JSContext* cx, WebGLProgram* program, const dom::Sequence<GLuint>& uniformIndices, GLenum pname, JS::MutableHandleValue retval) { retval.set(JS::NullValue()); if (IsContextLost()) return; if (!ValidateUniformEnum(this, pname, "getActiveUniforms")) return; if (!ValidateObject("getActiveUniforms: program", program)) return; size_t count = uniformIndices.Length(); if (!count) return; GLuint progname = program->mGLName; Vector<GLint> samples; if (!samples.resize(count)) { return; } MakeContextCurrent(); gl->fGetActiveUniformsiv(progname, count, uniformIndices.Elements(), pname, samples.begin()); JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, count)); if (!array) { return; } 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 (uint32_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 (uint32_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: return; } retval.setObjectOrNull(array); }