summaryrefslogtreecommitdiffstats
path: root/dom/canvas/WebGLProgram.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas/WebGLProgram.cpp')
-rw-r--r--dom/canvas/WebGLProgram.cpp1645
1 files changed, 1645 insertions, 0 deletions
diff --git a/dom/canvas/WebGLProgram.cpp b/dom/canvas/WebGLProgram.cpp
new file mode 100644
index 000000000..fa7997f22
--- /dev/null
+++ b/dom/canvas/WebGLProgram.cpp
@@ -0,0 +1,1645 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLProgram.h"
+
+#include "GLContext.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/dom/WebGL2RenderingContextBinding.h"
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "mozilla/RefPtr.h"
+#include "nsPrintfCString.h"
+#include "WebGLActiveInfo.h"
+#include "WebGLContext.h"
+#include "WebGLShader.h"
+#include "WebGLTransformFeedback.h"
+#include "WebGLUniformLocation.h"
+#include "WebGLValidateStrings.h"
+
+namespace mozilla {
+
+/* If `name`: "foo[3]"
+ * Then returns true, with
+ * `out_baseName`: "foo"
+ * `out_isArray`: true
+ * `out_index`: 3
+ *
+ * If `name`: "foo"
+ * Then returns true, with
+ * `out_baseName`: "foo"
+ * `out_isArray`: false
+ * `out_index`: 0
+ */
+static bool
+ParseName(const nsCString& name, nsCString* const out_baseName,
+ bool* const out_isArray, size_t* const out_arrayIndex)
+{
+ int32_t indexEnd = name.RFind("]");
+ if (indexEnd == -1 ||
+ (uint32_t)indexEnd != name.Length() - 1)
+ {
+ *out_baseName = name;
+ *out_isArray = false;
+ *out_arrayIndex = 0;
+ return true;
+ }
+
+ int32_t indexOpenBracket = name.RFind("[");
+ if (indexOpenBracket == -1)
+ return false;
+
+ uint32_t indexStart = indexOpenBracket + 1;
+ uint32_t indexLen = indexEnd - indexStart;
+ if (indexLen == 0)
+ return false;
+
+ const nsAutoCString indexStr(Substring(name, indexStart, indexLen));
+
+ nsresult errorcode;
+ int32_t indexNum = indexStr.ToInteger(&errorcode);
+ if (NS_FAILED(errorcode))
+ return false;
+
+ if (indexNum < 0)
+ return false;
+
+ *out_baseName = StringHead(name, indexOpenBracket);
+ *out_isArray = true;
+ *out_arrayIndex = indexNum;
+ return true;
+}
+
+static void
+AssembleName(const nsCString& baseName, bool isArray, size_t arrayIndex,
+ nsCString* const out_name)
+{
+ *out_name = baseName;
+ if (isArray) {
+ out_name->Append('[');
+ out_name->AppendInt(uint64_t(arrayIndex));
+ out_name->Append(']');
+ }
+}
+
+////
+
+static GLenum
+AttribBaseType(GLenum attribType)
+{
+ switch (attribType) {
+ case LOCAL_GL_FLOAT:
+ case LOCAL_GL_FLOAT_VEC2:
+ case LOCAL_GL_FLOAT_VEC3:
+ case LOCAL_GL_FLOAT_VEC4:
+
+ case LOCAL_GL_FLOAT_MAT2:
+ case LOCAL_GL_FLOAT_MAT2x3:
+ case LOCAL_GL_FLOAT_MAT2x4:
+
+ case LOCAL_GL_FLOAT_MAT3x2:
+ case LOCAL_GL_FLOAT_MAT3:
+ case LOCAL_GL_FLOAT_MAT3x4:
+
+ case LOCAL_GL_FLOAT_MAT4x2:
+ case LOCAL_GL_FLOAT_MAT4x3:
+ case LOCAL_GL_FLOAT_MAT4:
+ return LOCAL_GL_FLOAT;
+
+ case LOCAL_GL_INT:
+ case LOCAL_GL_INT_VEC2:
+ case LOCAL_GL_INT_VEC3:
+ case LOCAL_GL_INT_VEC4:
+ return LOCAL_GL_INT;
+
+ case LOCAL_GL_UNSIGNED_INT:
+ case LOCAL_GL_UNSIGNED_INT_VEC2:
+ case LOCAL_GL_UNSIGNED_INT_VEC3:
+ case LOCAL_GL_UNSIGNED_INT_VEC4:
+ return LOCAL_GL_UNSIGNED_INT;
+
+ default:
+ MOZ_ASSERT(false, "unexpected attrib elemType");
+ return 0;
+ }
+}
+
+////
+
+/*static*/ const webgl::UniformInfo::TexListT*
+webgl::UniformInfo::GetTexList(WebGLActiveInfo* activeInfo)
+{
+ const auto& webgl = activeInfo->mWebGL;
+
+ switch (activeInfo->mElemType) {
+ case LOCAL_GL_SAMPLER_2D:
+ case LOCAL_GL_SAMPLER_2D_SHADOW:
+ case LOCAL_GL_INT_SAMPLER_2D:
+ case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
+ return &webgl->mBound2DTextures;
+
+ case LOCAL_GL_SAMPLER_CUBE:
+ case LOCAL_GL_SAMPLER_CUBE_SHADOW:
+ case LOCAL_GL_INT_SAMPLER_CUBE:
+ case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
+ return &webgl->mBoundCubeMapTextures;
+
+ case LOCAL_GL_SAMPLER_3D:
+ case LOCAL_GL_INT_SAMPLER_3D:
+ case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
+ return &webgl->mBound3DTextures;
+
+ case LOCAL_GL_SAMPLER_2D_ARRAY:
+ case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
+ case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
+ case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ return &webgl->mBound2DArrayTextures;
+
+ default:
+ return nullptr;
+ }
+}
+
+webgl::UniformInfo::UniformInfo(WebGLActiveInfo* activeInfo)
+ : mActiveInfo(activeInfo)
+ , mSamplerTexList(GetTexList(activeInfo))
+{
+ if (mSamplerTexList) {
+ mSamplerValues.assign(mActiveInfo->mElemCount, 0);
+ }
+}
+
+//////////
+
+//#define DUMP_SHADERVAR_MAPPINGS
+
+static already_AddRefed<const webgl::LinkedProgramInfo>
+QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl)
+{
+ WebGLContext* const webgl = prog->mContext;
+
+ RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
+
+ GLuint maxAttribLenWithNull = 0;
+ gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
+ (GLint*)&maxAttribLenWithNull);
+ if (maxAttribLenWithNull < 1)
+ maxAttribLenWithNull = 1;
+
+ GLuint maxUniformLenWithNull = 0;
+ gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH,
+ (GLint*)&maxUniformLenWithNull);
+ if (maxUniformLenWithNull < 1)
+ maxUniformLenWithNull = 1;
+
+ GLuint maxUniformBlockLenWithNull = 0;
+ if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
+ gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH,
+ (GLint*)&maxUniformBlockLenWithNull);
+ if (maxUniformBlockLenWithNull < 1)
+ maxUniformBlockLenWithNull = 1;
+ }
+
+ GLuint maxTransformFeedbackVaryingLenWithNull = 0;
+ if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
+ gl->fGetProgramiv(prog->mGLName, LOCAL_GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH,
+ (GLint*)&maxTransformFeedbackVaryingLenWithNull);
+ if (maxTransformFeedbackVaryingLenWithNull < 1)
+ maxTransformFeedbackVaryingLenWithNull = 1;
+ }
+
+ // Attribs (can't be arrays)
+
+ GLuint numActiveAttribs = 0;
+ gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES,
+ (GLint*)&numActiveAttribs);
+
+ for (GLuint i = 0; i < numActiveAttribs; i++) {
+ nsAutoCString mappedName;
+ mappedName.SetLength(maxAttribLenWithNull - 1);
+
+ GLsizei lengthWithoutNull = 0;
+ GLint elemCount = 0; // `size`
+ GLenum elemType = 0; // `type`
+ gl->fGetActiveAttrib(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
+ &elemCount, &elemType, mappedName.BeginWriting());
+ GLenum error = gl->fGetError();
+ if (error != LOCAL_GL_NO_ERROR) {
+ gfxCriticalNote << "Failed to do glGetActiveAttrib: " << error;
+ }
+
+ mappedName.SetLength(lengthWithoutNull);
+
+ ////
+
+ nsCString userName;
+ if (!prog->FindAttribUserNameByMappedName(mappedName, &userName)) {
+ userName = mappedName;
+ }
+
+ ///////
+
+ GLint loc = gl->fGetAttribLocation(prog->mGLName,
+ mappedName.BeginReading());
+ if (gl->WorkAroundDriverBugs() &&
+ mappedName.EqualsIgnoreCase("gl_", 3))
+ {
+ // Bug 1328559: Appears problematic on ANGLE and OSX, but not Linux or Win+GL.
+ loc = -1;
+ }
+#ifdef DUMP_SHADERVAR_MAPPINGS
+ printf_stderr("[attrib %u/%u] @%i %s->%s\n", i, numActiveAttribs, loc,
+ userName.BeginReading(), mappedName.BeginReading());
+#endif
+ MOZ_ASSERT_IF(mappedName.EqualsIgnoreCase("gl_", 3), loc == -1);
+
+ ///////
+
+ const bool isArray = false;
+ const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
+ elemType, isArray,
+ userName,
+ mappedName);
+ const GLenum baseType = AttribBaseType(elemType);
+ const webgl::AttribInfo attrib = {activeInfo, loc, baseType};
+ info->attribs.push_back(attrib);
+ }
+
+ // Uniforms (can be basically anything)
+
+ const bool needsCheckForArrays = gl->WorkAroundDriverBugs();
+
+ GLuint numActiveUniforms = 0;
+ gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORMS,
+ (GLint*)&numActiveUniforms);
+
+ for (GLuint i = 0; i < numActiveUniforms; i++) {
+ nsAutoCString mappedName;
+ mappedName.SetLength(maxUniformLenWithNull - 1);
+
+ GLsizei lengthWithoutNull = 0;
+ GLint elemCount = 0; // `size`
+ GLenum elemType = 0; // `type`
+ gl->fGetActiveUniform(prog->mGLName, i, mappedName.Length()+1, &lengthWithoutNull,
+ &elemCount, &elemType, mappedName.BeginWriting());
+
+ mappedName.SetLength(lengthWithoutNull);
+
+ ///////
+
+ nsAutoCString baseMappedName;
+ bool isArray;
+ size_t arrayIndex;
+ if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
+ MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
+
+ // Note that for good drivers, `isArray` should already be correct.
+ // However, if FindUniform succeeds, it will be validator-guaranteed correct.
+
+ ///////
+
+ nsAutoCString baseUserName;
+ if (!prog->FindUniformByMappedName(baseMappedName, &baseUserName, &isArray)) {
+ // Validator likely missing.
+ baseUserName = baseMappedName;
+
+ if (needsCheckForArrays && !isArray) {
+ // By GLES 3, GetUniformLocation("foo[0]") should return -1 if `foo` is
+ // not an array. Our current linux Try slaves return the location of `foo`
+ // anyways, though.
+ std::string mappedNameStr = baseMappedName.BeginReading();
+ mappedNameStr += "[0]";
+
+ GLint loc = gl->fGetUniformLocation(prog->mGLName, mappedNameStr.c_str());
+ if (loc != -1)
+ isArray = true;
+ }
+ }
+
+ ///////
+
+#ifdef DUMP_SHADERVAR_MAPPINGS
+ printf_stderr("[uniform %u/%u] %s->%s\n", i, numActiveUniforms,
+ baseUserName.BeginReading(), mappedName.BeginReading());
+#endif
+
+ ///////
+
+ const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl, elemCount,
+ elemType, isArray,
+ baseUserName,
+ baseMappedName);
+
+ auto* uniform = new webgl::UniformInfo(activeInfo);
+ info->uniforms.push_back(uniform);
+
+ if (uniform->mSamplerTexList) {
+ info->uniformSamplers.push_back(uniform);
+ }
+ }
+
+ // Uniform Blocks (can be arrays, but can't contain sampler types)
+
+ if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
+ GLuint numActiveUniformBlocks = 0;
+ gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCKS,
+ (GLint*)&numActiveUniformBlocks);
+
+ for (GLuint i = 0; i < numActiveUniformBlocks; i++) {
+ nsAutoCString mappedName;
+ mappedName.SetLength(maxUniformBlockLenWithNull - 1);
+
+ GLint lengthWithoutNull;
+ gl->fGetActiveUniformBlockiv(prog->mGLName, i, LOCAL_GL_UNIFORM_BLOCK_NAME_LENGTH, &lengthWithoutNull);
+ gl->fGetActiveUniformBlockName(prog->mGLName, i, maxUniformBlockLenWithNull, &lengthWithoutNull, mappedName.BeginWriting());
+ mappedName.SetLength(lengthWithoutNull);
+
+ ////
+
+ nsCString userName;
+ if (!prog->UnmapUniformBlockName(mappedName, &userName))
+ continue;
+
+#ifdef DUMP_SHADERVAR_MAPPINGS
+ printf_stderr("[uniform block %u/%u] %s->%s\n", i, numActiveUniformBlocks,
+ userName.BeginReading(), mappedName.BeginReading());
+#endif
+
+ ////
+
+ GLuint dataSize = 0;
+ gl->fGetActiveUniformBlockiv(prog->mGLName, i,
+ LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE,
+ (GLint*)&dataSize);
+
+
+ auto* block = new webgl::UniformBlockInfo(webgl, userName, mappedName,
+ dataSize);
+ info->uniformBlocks.push_back(block);
+ }
+ }
+
+ // Transform feedback varyings (can be arrays)
+
+ if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
+ GLuint numTransformFeedbackVaryings = 0;
+ gl->fGetProgramiv(prog->mGLName, LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS,
+ (GLint*)&numTransformFeedbackVaryings);
+
+ for (GLuint i = 0; i < numTransformFeedbackVaryings; i++) {
+ nsAutoCString mappedName;
+ mappedName.SetLength(maxTransformFeedbackVaryingLenWithNull - 1);
+
+ GLint lengthWithoutNull;
+ GLsizei elemCount;
+ GLenum elemType;
+ gl->fGetTransformFeedbackVarying(prog->mGLName, i,
+ maxTransformFeedbackVaryingLenWithNull,
+ &lengthWithoutNull, &elemCount, &elemType,
+ mappedName.BeginWriting());
+ mappedName.SetLength(lengthWithoutNull);
+
+ ////
+
+ nsAutoCString baseMappedName;
+ bool isArray;
+ size_t arrayIndex;
+ if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
+ MOZ_CRASH("GFX: Failed to parse `mappedName` received from driver.");
+
+ nsAutoCString baseUserName;
+ if (!prog->FindVaryingByMappedName(mappedName, &baseUserName, &isArray)) {
+ baseUserName = baseMappedName;
+ }
+
+ ////
+
+#ifdef DUMP_SHADERVAR_MAPPINGS
+ printf_stderr("[transform feedback varying %u/%u] %s->%s\n", i,
+ numTransformFeedbackVaryings, baseUserName.BeginReading(),
+ mappedName.BeginReading());
+#endif
+
+ const RefPtr<WebGLActiveInfo> activeInfo = new WebGLActiveInfo(webgl,
+ elemCount,
+ elemType,
+ isArray,
+ baseUserName,
+ mappedName);
+ info->transformFeedbackVaryings.push_back(activeInfo);
+ }
+ }
+
+ // Frag outputs
+
+ prog->EnumerateFragOutputs(info->fragDataMap);
+
+ return info.forget();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
+ : prog(prog)
+ , transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode)
+{ }
+
+webgl::LinkedProgramInfo::~LinkedProgramInfo()
+{
+ for (auto& cur : uniforms) {
+ delete cur;
+ }
+ for (auto& cur : uniformBlocks) {
+ delete cur;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WebGLProgram
+
+static GLuint
+CreateProgram(gl::GLContext* gl)
+{
+ gl->MakeCurrent();
+ return gl->fCreateProgram();
+}
+
+WebGLProgram::WebGLProgram(WebGLContext* webgl)
+ : WebGLRefCountedObject(webgl)
+ , mGLName(CreateProgram(webgl->GL()))
+ , mNumActiveTFOs(0)
+ , mNextLink_TransformFeedbackBufferMode(LOCAL_GL_INTERLEAVED_ATTRIBS)
+{
+ mContext->mPrograms.insertBack(this);
+}
+
+WebGLProgram::~WebGLProgram()
+{
+ DeleteOnce();
+}
+
+void
+WebGLProgram::Delete()
+{
+ gl::GLContext* gl = mContext->GL();
+
+ gl->MakeCurrent();
+ gl->fDeleteProgram(mGLName);
+
+ mVertShader = nullptr;
+ mFragShader = nullptr;
+
+ mMostRecentLinkInfo = nullptr;
+
+ LinkedListElement<WebGLProgram>::removeFrom(mContext->mPrograms);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// GL funcs
+
+void
+WebGLProgram::AttachShader(WebGLShader* shader)
+{
+ WebGLRefPtr<WebGLShader>* shaderSlot;
+ switch (shader->mType) {
+ case LOCAL_GL_VERTEX_SHADER:
+ shaderSlot = &mVertShader;
+ break;
+ case LOCAL_GL_FRAGMENT_SHADER:
+ shaderSlot = &mFragShader;
+ break;
+ default:
+ mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
+ return;
+ }
+
+ if (*shaderSlot) {
+ if (shader == *shaderSlot) {
+ mContext->ErrorInvalidOperation("attachShader: `shader` is already attached.");
+ } else {
+ mContext->ErrorInvalidOperation("attachShader: Only one of each type of"
+ " shader may be attached to a program.");
+ }
+ return;
+ }
+
+ *shaderSlot = shader;
+
+ mContext->MakeContextCurrent();
+ mContext->gl->fAttachShader(mGLName, shader->mGLName);
+}
+
+void
+WebGLProgram::BindAttribLocation(GLuint loc, const nsAString& name)
+{
+ if (!ValidateGLSLVariableName(name, mContext, "bindAttribLocation"))
+ return;
+
+ if (loc >= mContext->MaxVertexAttribs()) {
+ mContext->ErrorInvalidValue("bindAttribLocation: `location` must be less than"
+ " MAX_VERTEX_ATTRIBS.");
+ return;
+ }
+
+ if (StringBeginsWith(name, NS_LITERAL_STRING("gl_"))) {
+ mContext->ErrorInvalidOperation("bindAttribLocation: Can't set the location of a"
+ " name that starts with 'gl_'.");
+ return;
+ }
+
+ NS_LossyConvertUTF16toASCII asciiName(name);
+
+ auto res = mNextLink_BoundAttribLocs.insert({asciiName, loc});
+
+ const bool wasInserted = res.second;
+ if (!wasInserted) {
+ auto itr = res.first;
+ itr->second = loc;
+ }
+}
+
+void
+WebGLProgram::DetachShader(const WebGLShader* shader)
+{
+ MOZ_ASSERT(shader);
+
+ WebGLRefPtr<WebGLShader>* shaderSlot;
+ switch (shader->mType) {
+ case LOCAL_GL_VERTEX_SHADER:
+ shaderSlot = &mVertShader;
+ break;
+ case LOCAL_GL_FRAGMENT_SHADER:
+ shaderSlot = &mFragShader;
+ break;
+ default:
+ mContext->ErrorInvalidOperation("attachShader: Bad type for shader.");
+ return;
+ }
+
+ if (*shaderSlot != shader) {
+ mContext->ErrorInvalidOperation("detachShader: `shader` is not attached.");
+ return;
+ }
+
+ *shaderSlot = nullptr;
+
+ mContext->MakeContextCurrent();
+ mContext->gl->fDetachShader(mGLName, shader->mGLName);
+}
+
+already_AddRefed<WebGLActiveInfo>
+WebGLProgram::GetActiveAttrib(GLuint index) const
+{
+ if (!mMostRecentLinkInfo) {
+ RefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
+ return ret.forget();
+ }
+
+ const auto& attribs = mMostRecentLinkInfo->attribs;
+
+ if (index >= attribs.size()) {
+ mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
+ index, "ACTIVE_ATTRIBS", attribs.size());
+ return nullptr;
+ }
+
+ RefPtr<WebGLActiveInfo> ret = attribs[index].mActiveInfo;
+ return ret.forget();
+}
+
+already_AddRefed<WebGLActiveInfo>
+WebGLProgram::GetActiveUniform(GLuint index) const
+{
+ if (!mMostRecentLinkInfo) {
+ // According to the spec, this can return null.
+ RefPtr<WebGLActiveInfo> ret = WebGLActiveInfo::CreateInvalid(mContext);
+ return ret.forget();
+ }
+
+ const auto& uniforms = mMostRecentLinkInfo->uniforms;
+
+ if (index >= uniforms.size()) {
+ mContext->ErrorInvalidValue("`index` (%i) must be less than %s (%i).",
+ index, "ACTIVE_UNIFORMS", uniforms.size());
+ return nullptr;
+ }
+
+ RefPtr<WebGLActiveInfo> ret = uniforms[index]->mActiveInfo;
+ return ret.forget();
+}
+
+void
+WebGLProgram::GetAttachedShaders(nsTArray<RefPtr<WebGLShader>>* const out) const
+{
+ out->TruncateLength(0);
+
+ if (mVertShader)
+ out->AppendElement(mVertShader);
+
+ if (mFragShader)
+ out->AppendElement(mFragShader);
+}
+
+GLint
+WebGLProgram::GetAttribLocation(const nsAString& userName_wide) const
+{
+ if (!ValidateGLSLVariableName(userName_wide, mContext, "getAttribLocation"))
+ return -1;
+
+ if (!IsLinked()) {
+ mContext->ErrorInvalidOperation("getAttribLocation: `program` must be linked.");
+ return -1;
+ }
+
+ const NS_LossyConvertUTF16toASCII userName(userName_wide);
+
+ const webgl::AttribInfo* info;
+ if (!LinkInfo()->FindAttrib(userName, &info))
+ return -1;
+
+ return GLint(info->mLoc);
+}
+
+static GLint
+GetFragDataByUserName(const WebGLProgram* prog,
+ const nsCString& userName)
+{
+ nsCString mappedName;
+ if (!prog->LinkInfo()->MapFragDataName(userName, &mappedName))
+ return -1;
+
+ return prog->mContext->gl->fGetFragDataLocation(prog->mGLName, mappedName.BeginReading());
+}
+
+GLint
+WebGLProgram::GetFragDataLocation(const nsAString& userName_wide) const
+{
+ if (!ValidateGLSLVariableName(userName_wide, mContext, "getFragDataLocation"))
+ return -1;
+
+ if (!IsLinked()) {
+ mContext->ErrorInvalidOperation("getFragDataLocation: `program` must be linked.");
+ return -1;
+ }
+
+
+ const auto& gl = mContext->gl;
+ gl->MakeCurrent();
+
+ const NS_LossyConvertUTF16toASCII userName(userName_wide);
+#ifdef XP_MACOSX
+ if (gl->WorkAroundDriverBugs()) {
+ // OSX doesn't return locs for indexed names, just the base names.
+ // Indicated by failure in: conformance2/programs/gl-get-frag-data-location.html
+ bool isArray;
+ size_t arrayIndex;
+ nsCString baseUserName;
+ if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
+ return -1;
+
+ if (arrayIndex >= mContext->mImplMaxDrawBuffers)
+ return -1;
+
+ const auto baseLoc = GetFragDataByUserName(this, baseUserName);
+ const auto loc = baseLoc + GLint(arrayIndex);
+ return loc;
+ }
+#endif
+ return GetFragDataByUserName(this, userName);
+}
+
+void
+WebGLProgram::GetProgramInfoLog(nsAString* const out) const
+{
+ CopyASCIItoUTF16(mLinkLog, *out);
+}
+
+static GLint
+GetProgramiv(gl::GLContext* gl, GLuint program, GLenum pname)
+{
+ GLint ret = 0;
+ gl->fGetProgramiv(program, pname, &ret);
+ return ret;
+}
+
+JS::Value
+WebGLProgram::GetProgramParameter(GLenum pname) const
+{
+ gl::GLContext* gl = mContext->gl;
+ gl->MakeCurrent();
+
+ if (mContext->IsWebGL2()) {
+ switch (pname) {
+ case LOCAL_GL_ACTIVE_UNIFORM_BLOCKS:
+ if (!IsLinked())
+ return JS::NumberValue(0);
+ return JS::NumberValue(LinkInfo()->uniformBlocks.size());
+
+ case LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS:
+ if (!IsLinked())
+ return JS::NumberValue(0);
+ return JS::NumberValue(LinkInfo()->transformFeedbackVaryings.size());
+
+ case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
+ if (!IsLinked())
+ return JS::NumberValue(LOCAL_GL_INTERLEAVED_ATTRIBS);
+ return JS::NumberValue(LinkInfo()->transformFeedbackBufferMode);
+ }
+ }
+
+ switch (pname) {
+ case LOCAL_GL_ATTACHED_SHADERS:
+ return JS::NumberValue( int(bool(mVertShader.get())) + int(bool(mFragShader)) );
+
+ case LOCAL_GL_ACTIVE_UNIFORMS:
+ if (!IsLinked())
+ return JS::NumberValue(0);
+ return JS::NumberValue(LinkInfo()->uniforms.size());
+
+ case LOCAL_GL_ACTIVE_ATTRIBUTES:
+ if (!IsLinked())
+ return JS::NumberValue(0);
+ return JS::NumberValue(LinkInfo()->attribs.size());
+
+ case LOCAL_GL_DELETE_STATUS:
+ return JS::BooleanValue(IsDeleteRequested());
+
+ case LOCAL_GL_LINK_STATUS:
+ return JS::BooleanValue(IsLinked());
+
+ case LOCAL_GL_VALIDATE_STATUS:
+#ifdef XP_MACOSX
+ // See comment in ValidateProgram.
+ if (gl->WorkAroundDriverBugs())
+ return JS::BooleanValue(true);
+#endif
+ // Todo: Implement this in our code.
+ return JS::BooleanValue(bool(GetProgramiv(gl, mGLName, pname)));
+
+ default:
+ mContext->ErrorInvalidEnumInfo("getProgramParameter: `pname`",
+ pname);
+ return JS::NullValue();
+ }
+}
+
+GLuint
+WebGLProgram::GetUniformBlockIndex(const nsAString& userName_wide) const
+{
+ if (!ValidateGLSLVariableName(userName_wide, mContext, "getUniformBlockIndex"))
+ return LOCAL_GL_INVALID_INDEX;
+
+ if (!IsLinked()) {
+ mContext->ErrorInvalidOperation("getUniformBlockIndex: `program` must be linked.");
+ return LOCAL_GL_INVALID_INDEX;
+ }
+
+ const NS_LossyConvertUTF16toASCII userName(userName_wide);
+
+ const webgl::UniformBlockInfo* info = nullptr;
+ for (const auto& cur : LinkInfo()->uniformBlocks) {
+ if (cur->mUserName == userName) {
+ info = cur;
+ break;
+ }
+ }
+ if (!info)
+ return LOCAL_GL_INVALID_INDEX;
+
+ const auto& mappedName = info->mMappedName;
+
+ gl::GLContext* gl = mContext->GL();
+ gl->MakeCurrent();
+ return gl->fGetUniformBlockIndex(mGLName, mappedName.BeginReading());
+}
+
+void
+WebGLProgram::GetActiveUniformBlockName(GLuint uniformBlockIndex, nsAString& retval) const
+{
+ if (!IsLinked()) {
+ mContext->ErrorInvalidOperation("getActiveUniformBlockName: `program` must be linked.");
+ return;
+ }
+
+ const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
+ GLuint uniformBlockCount = (GLuint) linkInfo->uniformBlocks.size();
+ if (uniformBlockIndex >= uniformBlockCount) {
+ mContext->ErrorInvalidValue("getActiveUniformBlockName: index %u invalid.", uniformBlockIndex);
+ return;
+ }
+
+ const auto& blockInfo = linkInfo->uniformBlocks[uniformBlockIndex];
+ retval.Assign(NS_ConvertASCIItoUTF16(blockInfo->mUserName));
+}
+
+JS::Value
+WebGLProgram::GetActiveUniformBlockParam(GLuint uniformBlockIndex, GLenum pname) const
+{
+ if (!IsLinked()) {
+ mContext->ErrorInvalidOperation("getActiveUniformBlockParameter: `program` must be linked.");
+ return JS::NullValue();
+ }
+
+ const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
+ GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
+ if (uniformBlockIndex >= uniformBlockCount) {
+ mContext->ErrorInvalidValue("getActiveUniformBlockParameter: index %u invalid.", uniformBlockIndex);
+ return JS::NullValue();
+ }
+
+ gl::GLContext* gl = mContext->GL();
+ GLint param = 0;
+
+ switch (pname) {
+ case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
+ case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
+ gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex, pname, &param);
+ return JS::BooleanValue(bool(param));
+
+ case LOCAL_GL_UNIFORM_BLOCK_BINDING:
+ case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE:
+ case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
+ gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex, pname, &param);
+ return JS::NumberValue(param);
+
+ default:
+ MOZ_CRASH("bad `pname`.");
+ }
+}
+
+JS::Value
+WebGLProgram::GetActiveUniformBlockActiveUniforms(JSContext* cx, GLuint uniformBlockIndex,
+ ErrorResult* const out_error) const
+{
+ const char funcName[] = "getActiveUniformBlockParameter";
+ if (!IsLinked()) {
+ mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName);
+ return JS::NullValue();
+ }
+
+ const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
+ GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
+ if (uniformBlockIndex >= uniformBlockCount) {
+ mContext->ErrorInvalidValue("%s: Index %u invalid.", funcName, uniformBlockIndex);
+ return JS::NullValue();
+ }
+
+ gl::GLContext* gl = mContext->GL();
+ GLint activeUniformCount = 0;
+ gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex,
+ LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS,
+ &activeUniformCount);
+ JS::RootedObject obj(cx, dom::Uint32Array::Create(cx, mContext, activeUniformCount,
+ nullptr));
+ if (!obj) {
+ *out_error = NS_ERROR_OUT_OF_MEMORY;
+ return JS::NullValue();
+ }
+
+ dom::Uint32Array result;
+ DebugOnly<bool> inited = result.Init(obj);
+ MOZ_ASSERT(inited);
+ result.ComputeLengthAndData();
+ gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex,
+ LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
+ (GLint*)result.Data());
+
+ return JS::ObjectValue(*obj);
+}
+
+already_AddRefed<WebGLUniformLocation>
+WebGLProgram::GetUniformLocation(const nsAString& userName_wide) const
+{
+ if (!ValidateGLSLVariableName(userName_wide, mContext, "getUniformLocation"))
+ return nullptr;
+
+ if (!IsLinked()) {
+ mContext->ErrorInvalidOperation("getUniformLocation: `program` must be linked.");
+ return nullptr;
+ }
+
+ const NS_LossyConvertUTF16toASCII userName(userName_wide);
+
+ // GLES 2.0.25, Section 2.10, p35
+ // If the the uniform location is an array, then the location of the first
+ // element of that array can be retrieved by either using the name of the
+ // uniform array, or the name of the uniform array appended with "[0]".
+ nsCString mappedName;
+ size_t arrayIndex;
+ webgl::UniformInfo* info;
+ if (!LinkInfo()->FindUniform(userName, &mappedName, &arrayIndex, &info))
+ return nullptr;
+
+ gl::GLContext* gl = mContext->GL();
+ gl->MakeCurrent();
+
+ GLint loc = gl->fGetUniformLocation(mGLName, mappedName.BeginReading());
+ if (loc == -1)
+ return nullptr;
+
+ RefPtr<WebGLUniformLocation> locObj = new WebGLUniformLocation(mContext, LinkInfo(),
+ info, loc, arrayIndex);
+ return locObj.forget();
+}
+
+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]);
+
+ nsCString mappedName;
+ size_t arrayIndex;
+ webgl::UniformInfo* info;
+ if (!LinkInfo()->FindUniform(userName, &mappedName, &arrayIndex, &info)) {
+ arr.AppendElement(LOCAL_GL_INVALID_INDEX);
+ continue;
+ }
+
+ const GLchar* const mappedNameBegin = mappedName.get();
+
+ GLuint index = LOCAL_GL_INVALID_INDEX;
+ gl->fGetUniformIndices(mGLName, 1, &mappedNameBegin, &index);
+ arr.AppendElement(index);
+ }
+}
+
+void
+WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex,
+ GLuint uniformBlockBinding) const
+{
+ const char funcName[] = "getActiveUniformBlockName";
+ if (!IsLinked()) {
+ mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName);
+ return;
+ }
+
+ const auto& uniformBlocks = LinkInfo()->uniformBlocks;
+ if (uniformBlockIndex >= uniformBlocks.size()) {
+ mContext->ErrorInvalidValue("%s: Index %u invalid.", funcName, uniformBlockIndex);
+ return;
+ }
+ const auto& uniformBlock = uniformBlocks[uniformBlockIndex];
+
+ const auto& indexedBindings = mContext->mIndexedUniformBufferBindings;
+ if (uniformBlockBinding >= indexedBindings.size()) {
+ mContext->ErrorInvalidValue("%s: Binding %u invalid.", funcName,
+ uniformBlockBinding);
+ return;
+ }
+ const auto& indexedBinding = indexedBindings[uniformBlockBinding];
+
+ ////
+
+ gl::GLContext* gl = mContext->GL();
+ gl->MakeCurrent();
+ gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
+
+ ////
+
+ uniformBlock->mBinding = &indexedBinding;
+}
+
+bool
+WebGLProgram::ValidateForLink()
+{
+ if (!mVertShader || !mVertShader->IsCompiled()) {
+ mLinkLog.AssignLiteral("Must have a compiled vertex shader attached.");
+ return false;
+ }
+
+ if (!mFragShader || !mFragShader->IsCompiled()) {
+ mLinkLog.AssignLiteral("Must have an compiled fragment shader attached.");
+ return false;
+ }
+
+ if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog))
+ return false;
+
+ const auto& gl = mContext->gl;
+
+ if (gl->WorkAroundDriverBugs() &&
+ mContext->mIsMesa)
+ {
+ // Bug 777028: Mesa can't handle more than 16 samplers per program,
+ // counting each array entry.
+ size_t numSamplerUniforms_upperBound = mVertShader->CalcNumSamplerUniforms() +
+ mFragShader->CalcNumSamplerUniforms();
+ if (numSamplerUniforms_upperBound > 16) {
+ mLinkLog.AssignLiteral("Programs with more than 16 samplers are disallowed on"
+ " Mesa drivers to avoid crashing.");
+ return false;
+ }
+
+ // Bug 1203135: Mesa crashes internally if we exceed the reported maximum attribute count.
+ if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
+ mLinkLog.AssignLiteral("Number of attributes exceeds Mesa's reported max"
+ " attribute count.");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+WebGLProgram::LinkProgram()
+{
+ const char funcName[] = "linkProgram";
+
+ if (mNumActiveTFOs) {
+ mContext->ErrorInvalidOperation("%s: Program is in-use by one or more active"
+ " transform feedback objects.",
+ funcName);
+ return;
+ }
+
+ mContext->MakeContextCurrent();
+ mContext->InvalidateBufferFetching(); // we do it early in this function
+ // as some of the validation changes program state
+
+ mLinkLog.Truncate();
+ mMostRecentLinkInfo = nullptr;
+
+ if (!ValidateForLink()) {
+ mContext->GenerateWarning("%s: %s", funcName, mLinkLog.BeginReading());
+ return;
+ }
+
+ // Bind the attrib locations.
+ // This can't be done trivially, because we have to deal with mapped attrib names.
+ for (const auto& pair : mNextLink_BoundAttribLocs) {
+ const auto& name = pair.first;
+ const auto& index = pair.second;
+
+ mVertShader->BindAttribLocation(mGLName, name, index);
+ }
+
+ // Storage for transform feedback varyings before link.
+ // (Work around for bug seen on nVidia drivers.)
+ std::vector<std::string> scopedMappedTFVaryings;
+
+ if (mContext->IsWebGL2()) {
+ mVertShader->MapTransformFeedbackVaryings(mNextLink_TransformFeedbackVaryings,
+ &scopedMappedTFVaryings);
+
+ std::vector<const char*> driverVaryings;
+ driverVaryings.reserve(scopedMappedTFVaryings.size());
+ for (const auto& cur : scopedMappedTFVaryings) {
+ driverVaryings.push_back(cur.c_str());
+ }
+
+ mContext->gl->fTransformFeedbackVaryings(mGLName, driverVaryings.size(),
+ driverVaryings.data(),
+ mNextLink_TransformFeedbackBufferMode);
+ }
+
+ LinkAndUpdate();
+
+ if (mMostRecentLinkInfo) {
+ nsCString postLinkLog;
+ if (ValidateAfterTentativeLink(&postLinkLog))
+ return;
+
+ mMostRecentLinkInfo = nullptr;
+ mLinkLog = postLinkLog;
+ }
+
+ // Failed link.
+ if (mContext->ShouldGenerateWarnings()) {
+ // report shader/program infoLogs as warnings.
+ // note that shader compilation errors can be deferred to linkProgram,
+ // which is why we can't do anything in compileShader. In practice we could
+ // report in compileShader the translation errors generated by ANGLE,
+ // but it seems saner to keep a single way of obtaining shader infologs.
+ if (!mLinkLog.IsEmpty()) {
+ mContext->GenerateWarning("linkProgram: Failed to link, leaving the following"
+ " log:\n%s\n",
+ mLinkLog.BeginReading());
+ }
+ }
+}
+
+static uint8_t
+NumUsedLocationsByElemType(GLenum elemType)
+{
+ // GLES 3.0.4 p55
+
+ switch (elemType) {
+ case LOCAL_GL_FLOAT_MAT2:
+ case LOCAL_GL_FLOAT_MAT2x3:
+ case LOCAL_GL_FLOAT_MAT2x4:
+ return 2;
+
+ case LOCAL_GL_FLOAT_MAT3x2:
+ case LOCAL_GL_FLOAT_MAT3:
+ case LOCAL_GL_FLOAT_MAT3x4:
+ return 3;
+
+ case LOCAL_GL_FLOAT_MAT4x2:
+ case LOCAL_GL_FLOAT_MAT4x3:
+ case LOCAL_GL_FLOAT_MAT4:
+ return 4;
+
+ default:
+ return 1;
+ }
+}
+
+static uint8_t
+NumComponents(GLenum elemType)
+{
+ switch (elemType) {
+ case LOCAL_GL_FLOAT:
+ case LOCAL_GL_INT:
+ case LOCAL_GL_UNSIGNED_INT:
+ case LOCAL_GL_BOOL:
+ return 1;
+
+ case LOCAL_GL_FLOAT_VEC2:
+ case LOCAL_GL_INT_VEC2:
+ case LOCAL_GL_UNSIGNED_INT_VEC2:
+ case LOCAL_GL_BOOL_VEC2:
+ return 2;
+
+ case LOCAL_GL_FLOAT_VEC3:
+ case LOCAL_GL_INT_VEC3:
+ case LOCAL_GL_UNSIGNED_INT_VEC3:
+ case LOCAL_GL_BOOL_VEC3:
+ return 3;
+
+ case LOCAL_GL_FLOAT_VEC4:
+ case LOCAL_GL_INT_VEC4:
+ case LOCAL_GL_UNSIGNED_INT_VEC4:
+ case LOCAL_GL_BOOL_VEC4:
+ case LOCAL_GL_FLOAT_MAT2:
+ return 4;
+
+ case LOCAL_GL_FLOAT_MAT2x3:
+ case LOCAL_GL_FLOAT_MAT3x2:
+ return 6;
+
+ case LOCAL_GL_FLOAT_MAT2x4:
+ case LOCAL_GL_FLOAT_MAT4x2:
+ return 8;
+
+ case LOCAL_GL_FLOAT_MAT3:
+ return 9;
+
+ case LOCAL_GL_FLOAT_MAT3x4:
+ case LOCAL_GL_FLOAT_MAT4x3:
+ return 12;
+
+ case LOCAL_GL_FLOAT_MAT4:
+ return 16;
+
+ default:
+ MOZ_CRASH("`elemType`");
+ }
+}
+
+bool
+WebGLProgram::ValidateAfterTentativeLink(nsCString* const out_linkLog) const
+{
+ const auto& linkInfo = mMostRecentLinkInfo;
+ const auto& gl = mContext->gl;
+
+ // Check if the attrib name conflicting to uniform name
+ for (const auto& attrib : linkInfo->attribs) {
+ const auto& attribName = attrib.mActiveInfo->mBaseUserName;
+
+ for (const auto& uniform : linkInfo->uniforms) {
+ const auto& uniformName = uniform->mActiveInfo->mBaseUserName;
+ if (attribName == uniformName) {
+ *out_linkLog = nsPrintfCString("Attrib name conflicts with uniform name:"
+ " %s",
+ attribName.BeginReading());
+ return false;
+ }
+ }
+ }
+
+ std::map<uint32_t, const webgl::AttribInfo*> attribsByLoc;
+ for (const auto& attrib : linkInfo->attribs) {
+ if (attrib.mLoc == -1)
+ continue;
+
+ const auto& elemType = attrib.mActiveInfo->mElemType;
+ const auto numUsedLocs = NumUsedLocationsByElemType(elemType);
+ for (uint32_t i = 0; i < numUsedLocs; i++) {
+ const uint32_t usedLoc = attrib.mLoc + i;
+
+ const auto res = attribsByLoc.insert({usedLoc, &attrib});
+ const bool& didInsert = res.second;
+ if (!didInsert) {
+ const auto& aliasingName = attrib.mActiveInfo->mBaseUserName;
+ const auto& itrExisting = res.first;
+ const auto& existingInfo = itrExisting->second;
+ const auto& existingName = existingInfo->mActiveInfo->mBaseUserName;
+ *out_linkLog = nsPrintfCString("Attrib \"%s\" aliases locations used by"
+ " attrib \"%s\".",
+ aliasingName.BeginReading(),
+ existingName.BeginReading());
+ return false;
+ }
+ }
+ }
+
+ // Forbid:
+ // * Unrecognized varying name
+ // * Duplicate varying name
+ // * Too many components for specified buffer mode
+ if (mNextLink_TransformFeedbackVaryings.size()) {
+ GLuint maxComponentsPerIndex = 0;
+ switch (mNextLink_TransformFeedbackBufferMode) {
+ case LOCAL_GL_INTERLEAVED_ATTRIBS:
+ gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,
+ &maxComponentsPerIndex);
+ break;
+
+ case LOCAL_GL_SEPARATE_ATTRIBS:
+ gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,
+ &maxComponentsPerIndex);
+ break;
+
+ default:
+ MOZ_CRASH("`bufferMode`");
+ }
+
+ std::vector<size_t> componentsPerVert;
+ std::set<const WebGLActiveInfo*> alreadyUsed;
+ for (const auto& wideUserName : mNextLink_TransformFeedbackVaryings) {
+ if (!componentsPerVert.size() ||
+ mNextLink_TransformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS)
+ {
+ componentsPerVert.push_back(0);
+ }
+
+ ////
+
+ const WebGLActiveInfo* curInfo = nullptr;
+ for (const auto& info : linkInfo->transformFeedbackVaryings) {
+ const NS_ConvertASCIItoUTF16 info_wideUserName(info->mBaseUserName);
+ if (info_wideUserName == wideUserName) {
+ curInfo = info.get();
+ break;
+ }
+ }
+
+ if (!curInfo) {
+ const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
+ *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\" not"
+ " found.",
+ asciiUserName.BeginReading());
+ return false;
+ }
+
+ const auto insertResPair = alreadyUsed.insert(curInfo);
+ const auto& didInsert = insertResPair.second;
+ if (!didInsert) {
+ const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
+ *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\""
+ " specified twice.",
+ asciiUserName.BeginReading());
+ return false;
+ }
+
+ ////
+
+ size_t varyingComponents = NumComponents(curInfo->mElemType);
+ varyingComponents *= curInfo->mElemCount;
+
+ auto& totalComponentsForIndex = *(componentsPerVert.rbegin());
+ totalComponentsForIndex += varyingComponents;
+
+ if (totalComponentsForIndex > maxComponentsPerIndex) {
+ const NS_LossyConvertUTF16toASCII asciiUserName(wideUserName);
+ *out_linkLog = nsPrintfCString("Transform feedback varying \"%s\""
+ " pushed `componentsForIndex` over the"
+ " limit of %u.",
+ asciiUserName.BeginReading(),
+ maxComponentsPerIndex);
+ return false;
+ }
+ }
+
+ linkInfo->componentsPerTFVert.swap(componentsPerVert);
+ }
+
+ return true;
+}
+
+bool
+WebGLProgram::UseProgram() const
+{
+ const char funcName[] = "useProgram";
+
+ if (!mMostRecentLinkInfo) {
+ mContext->ErrorInvalidOperation("%s: Program has not been successfully linked.",
+ funcName);
+ return false;
+ }
+
+ if (mContext->mBoundTransformFeedback &&
+ mContext->mBoundTransformFeedback->mIsActive &&
+ !mContext->mBoundTransformFeedback->mIsPaused)
+ {
+ mContext->ErrorInvalidOperation("%s: Transform feedback active and not paused.",
+ funcName);
+ return false;
+ }
+
+ mContext->MakeContextCurrent();
+
+ mContext->InvalidateBufferFetching();
+
+ mContext->gl->fUseProgram(mGLName);
+ return true;
+}
+
+void
+WebGLProgram::ValidateProgram() const
+{
+ mContext->MakeContextCurrent();
+ gl::GLContext* gl = mContext->gl;
+
+#ifdef XP_MACOSX
+ // See bug 593867 for NVIDIA and bug 657201 for ATI. The latter is confirmed
+ // with Mac OS 10.6.7.
+ if (gl->WorkAroundDriverBugs()) {
+ mContext->GenerateWarning("validateProgram: Implemented as a no-op on"
+ " Mac to work around crashes.");
+ return;
+ }
+#endif
+
+ gl->fValidateProgram(mGLName);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+void
+WebGLProgram::LinkAndUpdate()
+{
+ mMostRecentLinkInfo = nullptr;
+
+ gl::GLContext* gl = mContext->gl;
+ gl->fLinkProgram(mGLName);
+
+ // Grab the program log.
+ GLuint logLenWithNull = 0;
+ gl->fGetProgramiv(mGLName, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&logLenWithNull);
+ if (logLenWithNull > 1) {
+ mLinkLog.SetLength(logLenWithNull - 1);
+ gl->fGetProgramInfoLog(mGLName, logLenWithNull, nullptr, mLinkLog.BeginWriting());
+ } else {
+ mLinkLog.SetLength(0);
+ }
+
+ GLint ok = 0;
+ gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
+ if (!ok)
+ return;
+
+ mMostRecentLinkInfo = QueryProgramInfo(this, gl);
+ MOZ_RELEASE_ASSERT(mMostRecentLinkInfo, "GFX: most recent link info not set.");
+}
+
+bool
+WebGLProgram::FindAttribUserNameByMappedName(const nsACString& mappedName,
+ nsCString* const out_userName) const
+{
+ if (mVertShader->FindAttribUserNameByMappedName(mappedName, out_userName))
+ return true;
+
+ return false;
+}
+
+bool
+WebGLProgram::FindVaryingByMappedName(const nsACString& mappedName,
+ nsCString* const out_userName,
+ bool* const out_isArray) const
+{
+ if (mVertShader->FindVaryingByMappedName(mappedName, out_userName, out_isArray))
+ return true;
+
+ return false;
+}
+
+
+bool
+WebGLProgram::FindUniformByMappedName(const nsACString& mappedName,
+ nsCString* const out_userName,
+ bool* const out_isArray) const
+{
+ if (mVertShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
+ return true;
+
+ if (mFragShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
+ return true;
+
+ return false;
+}
+
+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;
+}
+
+already_AddRefed<WebGLActiveInfo>
+WebGLProgram::GetTransformFeedbackVarying(GLuint index) const
+{
+ // No docs in the WebGL 2 spec for this function. Taking the language for
+ // getActiveAttrib, which states that the function returns null on any error.
+ if (!IsLinked()) {
+ mContext->ErrorInvalidOperation("getTransformFeedbackVarying: `program` must be "
+ "linked.");
+ return nullptr;
+ }
+
+ if (index >= LinkInfo()->transformFeedbackVaryings.size()) {
+ mContext->ErrorInvalidValue("getTransformFeedbackVarying: `index` is greater or "
+ "equal to TRANSFORM_FEEDBACK_VARYINGS.");
+ return nullptr;
+ }
+
+ RefPtr<WebGLActiveInfo> ret = LinkInfo()->transformFeedbackVaryings[index];
+ return ret.forget();
+}
+
+bool
+WebGLProgram::UnmapUniformBlockName(const nsCString& mappedName,
+ nsCString* const out_userName) const
+{
+ nsCString baseMappedName;
+ bool isArray;
+ size_t arrayIndex;
+ if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
+ return false;
+
+ nsCString baseUserName;
+ if (!mVertShader->UnmapUniformBlockName(baseMappedName, &baseUserName) &&
+ !mFragShader->UnmapUniformBlockName(baseMappedName, &baseUserName))
+ {
+ return false;
+ }
+
+ AssembleName(baseUserName, isArray, arrayIndex, out_userName);
+ return true;
+}
+
+void
+WebGLProgram::EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const
+{
+ MOZ_ASSERT(mFragShader);
+
+ mFragShader->EnumerateFragOutputs(out_FragOutputs);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool
+IsBaseName(const nsCString& name)
+{
+ if (!name.Length())
+ return true;
+
+ return name[name.Length() - 1] != ']'; // Doesn't end in ']'.
+}
+
+bool
+webgl::LinkedProgramInfo::FindAttrib(const nsCString& userName,
+ const webgl::AttribInfo** const out) const
+{
+ // VS inputs cannot be arrays or structures.
+ // `userName` is thus always `baseUserName`.
+ for (const auto& attrib : attribs) {
+ if (attrib.mActiveInfo->mBaseUserName == userName) {
+ *out = &attrib;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+webgl::LinkedProgramInfo::FindUniform(const nsCString& userName,
+ nsCString* const out_mappedName,
+ size_t* const out_arrayIndex,
+ webgl::UniformInfo** const out_info) const
+{
+ nsCString baseUserName;
+ bool isArray;
+ size_t arrayIndex;
+ if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
+ return false;
+
+ webgl::UniformInfo* info = nullptr;
+ for (const auto& uniform : uniforms) {
+ if (uniform->mActiveInfo->mBaseUserName == baseUserName) {
+ info = uniform;
+ break;
+ }
+ }
+ if (!info)
+ return false;
+
+ const auto& baseMappedName = info->mActiveInfo->mBaseMappedName;
+ AssembleName(baseMappedName, isArray, arrayIndex, out_mappedName);
+
+ *out_arrayIndex = arrayIndex;
+ *out_info = info;
+ return true;
+}
+
+bool
+webgl::LinkedProgramInfo::MapFragDataName(const nsCString& userName,
+ nsCString* const out_mappedName) const
+{
+ // FS outputs can be arrays, but not structures.
+
+ if (!fragDataMap.size()) {
+ // No mappings map from validation, so just forward it.
+ *out_mappedName = userName;
+ return true;
+ }
+
+ nsCString baseUserName;
+ bool isArray;
+ size_t arrayIndex;
+ if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
+ return false;
+
+ const auto itr = fragDataMap.find(baseUserName);
+ if (itr == fragDataMap.end())
+ return false;
+
+ const auto& baseMappedName = itr->second;
+ AssembleName(baseMappedName, isArray, arrayIndex, out_mappedName);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+JSObject*
+WebGLProgram::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
+{
+ return dom::WebGLProgramBinding::Wrap(js, this, givenProto);
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLProgram, mVertShader, mFragShader)
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLProgram, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLProgram, Release)
+
+} // namespace mozilla