forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1210 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1210 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "WebGLProgram.h"
 | |
| 
 | |
| #include "GLContext.h"
 | |
| #include "mozilla/CheckedInt.h"
 | |
| #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 | |
| #include "mozilla/dom/WebGLRenderingContextBinding.h"
 | |
| #include "mozilla/gfx/Logging.h"
 | |
| #include "mozilla/RefPtr.h"
 | |
| #include "nsPrintfCString.h"
 | |
| #include "WebGLBuffer.h"
 | |
| #include "WebGLContext.h"
 | |
| #include "WebGLShader.h"
 | |
| #include "WebGLShaderValidator.h"
 | |
| #include "WebGLTransformFeedback.h"
 | |
| #include "WebGLValidateStrings.h"
 | |
| #include "WebGLVertexArray.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| static bool IsShadowSampler(const GLenum elemType) {
 | |
|   switch (elemType) {
 | |
|     case LOCAL_GL_SAMPLER_2D_SHADOW:
 | |
|     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
 | |
|     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
 | |
|       return true;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static Maybe<webgl::TextureBaseType> SamplerBaseType(const GLenum elemType) {
 | |
|   switch (elemType) {
 | |
|     case LOCAL_GL_SAMPLER_2D:
 | |
|     case LOCAL_GL_SAMPLER_3D:
 | |
|     case LOCAL_GL_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_SAMPLER_2D_ARRAY:
 | |
|     case LOCAL_GL_SAMPLER_2D_SHADOW:
 | |
|     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
 | |
|     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
 | |
|       return Some(webgl::TextureBaseType::Float);
 | |
| 
 | |
|     case LOCAL_GL_INT_SAMPLER_2D:
 | |
|     case LOCAL_GL_INT_SAMPLER_3D:
 | |
|     case LOCAL_GL_INT_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
 | |
|       return Some(webgl::TextureBaseType::Int);
 | |
| 
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
 | |
|       return Some(webgl::TextureBaseType::UInt);
 | |
| 
 | |
|     default:
 | |
|       return {};
 | |
|   }
 | |
| }
 | |
| 
 | |
| //////////
 | |
| 
 | |
| static webgl::TextureBaseType FragOutputBaseType(const GLenum type) {
 | |
|   switch (type) {
 | |
|     case LOCAL_GL_FLOAT:
 | |
|     case LOCAL_GL_FLOAT_VEC2:
 | |
|     case LOCAL_GL_FLOAT_VEC3:
 | |
|     case LOCAL_GL_FLOAT_VEC4:
 | |
|       return webgl::TextureBaseType::Float;
 | |
| 
 | |
|     case LOCAL_GL_INT:
 | |
|     case LOCAL_GL_INT_VEC2:
 | |
|     case LOCAL_GL_INT_VEC3:
 | |
|     case LOCAL_GL_INT_VEC4:
 | |
|       return webgl::TextureBaseType::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 webgl::TextureBaseType::UInt;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   const auto& str = EnumString(type);
 | |
|   gfxCriticalError() << "Unhandled enum for FragOutputBaseType: "
 | |
|                      << str.c_str();
 | |
|   return webgl::TextureBaseType::Float;
 | |
| }
 | |
| 
 | |
| // -----------------------------------------
 | |
| 
 | |
| namespace webgl {
 | |
| 
 | |
| void UniformAs1fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                   bool transpose, const void* any) {
 | |
|   gl.fUniform1fv(location, count, static_cast<const float*>(any));
 | |
| }
 | |
| void UniformAs2fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                   bool transpose, const void* any) {
 | |
|   gl.fUniform2fv(location, count, static_cast<const float*>(any));
 | |
| }
 | |
| void UniformAs3fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                   bool transpose, const void* any) {
 | |
|   gl.fUniform3fv(location, count, static_cast<const float*>(any));
 | |
| }
 | |
| void UniformAs4fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                   bool transpose, const void* any) {
 | |
|   gl.fUniform4fv(location, count, static_cast<const float*>(any));
 | |
| }
 | |
| 
 | |
| void UniformAs1iv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                   bool transpose, const void* any) {
 | |
|   gl.fUniform1iv(location, count, static_cast<const int32_t*>(any));
 | |
| }
 | |
| void UniformAs2iv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                   bool transpose, const void* any) {
 | |
|   gl.fUniform2iv(location, count, static_cast<const int32_t*>(any));
 | |
| }
 | |
| void UniformAs3iv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                   bool transpose, const void* any) {
 | |
|   gl.fUniform3iv(location, count, static_cast<const int32_t*>(any));
 | |
| }
 | |
| void UniformAs4iv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                   bool transpose, const void* any) {
 | |
|   gl.fUniform4iv(location, count, static_cast<const int32_t*>(any));
 | |
| }
 | |
| 
 | |
| void UniformAs1uiv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                    bool transpose, const void* any) {
 | |
|   gl.fUniform1uiv(location, count, static_cast<const uint32_t*>(any));
 | |
| }
 | |
| void UniformAs2uiv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                    bool transpose, const void* any) {
 | |
|   gl.fUniform2uiv(location, count, static_cast<const uint32_t*>(any));
 | |
| }
 | |
| void UniformAs3uiv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                    bool transpose, const void* any) {
 | |
|   gl.fUniform3uiv(location, count, static_cast<const uint32_t*>(any));
 | |
| }
 | |
| void UniformAs4uiv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                    bool transpose, const void* any) {
 | |
|   gl.fUniform4uiv(location, count, static_cast<const uint32_t*>(any));
 | |
| }
 | |
| 
 | |
| void UniformAsMatrix2x2fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                           bool transpose, const void* any) {
 | |
|   gl.fUniformMatrix2fv(location, count, transpose,
 | |
|                        static_cast<const float*>(any));
 | |
| }
 | |
| void UniformAsMatrix2x3fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                           bool transpose, const void* any) {
 | |
|   gl.fUniformMatrix2x3fv(location, count, transpose,
 | |
|                          static_cast<const float*>(any));
 | |
| }
 | |
| void UniformAsMatrix2x4fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                           bool transpose, const void* any) {
 | |
|   gl.fUniformMatrix2x4fv(location, count, transpose,
 | |
|                          static_cast<const float*>(any));
 | |
| }
 | |
| 
 | |
| void UniformAsMatrix3x2fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                           bool transpose, const void* any) {
 | |
|   gl.fUniformMatrix3x2fv(location, count, transpose,
 | |
|                          static_cast<const float*>(any));
 | |
| }
 | |
| void UniformAsMatrix3x3fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                           bool transpose, const void* any) {
 | |
|   gl.fUniformMatrix3fv(location, count, transpose,
 | |
|                        static_cast<const float*>(any));
 | |
| }
 | |
| void UniformAsMatrix3x4fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                           bool transpose, const void* any) {
 | |
|   gl.fUniformMatrix3x4fv(location, count, transpose,
 | |
|                          static_cast<const float*>(any));
 | |
| }
 | |
| 
 | |
| void UniformAsMatrix4x2fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                           bool transpose, const void* any) {
 | |
|   gl.fUniformMatrix4x2fv(location, count, transpose,
 | |
|                          static_cast<const float*>(any));
 | |
| }
 | |
| void UniformAsMatrix4x3fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                           bool transpose, const void* any) {
 | |
|   gl.fUniformMatrix4x3fv(location, count, transpose,
 | |
|                          static_cast<const float*>(any));
 | |
| }
 | |
| void UniformAsMatrix4x4fv(gl::GLContext& gl, GLint location, GLsizei count,
 | |
|                           bool transpose, const void* any) {
 | |
|   gl.fUniformMatrix4fv(location, count, transpose,
 | |
|                        static_cast<const float*>(any));
 | |
| }
 | |
| 
 | |
| // -
 | |
| 
 | |
| static bool EndsWith(const std::string& str, const std::string& needle) {
 | |
|   if (str.length() < needle.length()) return false;
 | |
|   return str.compare(str.length() - needle.length(), needle.length(), needle) ==
 | |
|          0;
 | |
| }
 | |
| 
 | |
| webgl::ActiveUniformValidationInfo webgl::ActiveUniformValidationInfo::Make(
 | |
|     const webgl::ActiveUniformInfo& info) {
 | |
|   auto ret = webgl::ActiveUniformValidationInfo{info};
 | |
|   ret.isArray = EndsWith(info.name, "[0]");
 | |
| 
 | |
|   switch (info.elemType) {
 | |
|     case LOCAL_GL_FLOAT:
 | |
|       ret.channelsPerElem = 1;
 | |
|       ret.pfn = &UniformAs1fv;
 | |
|       break;
 | |
|     case LOCAL_GL_FLOAT_VEC2:
 | |
|       ret.channelsPerElem = 2;
 | |
|       ret.pfn = &UniformAs2fv;
 | |
|       break;
 | |
|     case LOCAL_GL_FLOAT_VEC3:
 | |
|       ret.channelsPerElem = 3;
 | |
|       ret.pfn = &UniformAs3fv;
 | |
|       break;
 | |
|     case LOCAL_GL_FLOAT_VEC4:
 | |
|       ret.channelsPerElem = 4;
 | |
|       ret.pfn = &UniformAs4fv;
 | |
|       break;
 | |
| 
 | |
|     case LOCAL_GL_SAMPLER_2D:
 | |
|     case LOCAL_GL_SAMPLER_3D:
 | |
|     case LOCAL_GL_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_SAMPLER_2D_SHADOW:
 | |
|     case LOCAL_GL_SAMPLER_2D_ARRAY:
 | |
|     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
 | |
|     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
 | |
|     case LOCAL_GL_INT_SAMPLER_2D:
 | |
|     case LOCAL_GL_INT_SAMPLER_3D:
 | |
|     case LOCAL_GL_INT_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
 | |
|     case LOCAL_GL_BOOL:
 | |
|     case LOCAL_GL_INT:
 | |
|       ret.channelsPerElem = 1;
 | |
|       ret.pfn = &UniformAs1iv;
 | |
|       break;
 | |
|     case LOCAL_GL_BOOL_VEC2:
 | |
|     case LOCAL_GL_INT_VEC2:
 | |
|       ret.channelsPerElem = 2;
 | |
|       ret.pfn = &UniformAs2iv;
 | |
|       break;
 | |
|     case LOCAL_GL_BOOL_VEC3:
 | |
|     case LOCAL_GL_INT_VEC3:
 | |
|       ret.channelsPerElem = 3;
 | |
|       ret.pfn = &UniformAs3iv;
 | |
|       break;
 | |
|     case LOCAL_GL_BOOL_VEC4:
 | |
|     case LOCAL_GL_INT_VEC4:
 | |
|       ret.channelsPerElem = 4;
 | |
|       ret.pfn = &UniformAs4iv;
 | |
|       break;
 | |
| 
 | |
|     case LOCAL_GL_UNSIGNED_INT:
 | |
|       ret.channelsPerElem = 1;
 | |
|       ret.pfn = &UniformAs1uiv;
 | |
|       break;
 | |
|     case LOCAL_GL_UNSIGNED_INT_VEC2:
 | |
|       ret.channelsPerElem = 2;
 | |
|       ret.pfn = &UniformAs2uiv;
 | |
|       break;
 | |
|     case LOCAL_GL_UNSIGNED_INT_VEC3:
 | |
|       ret.channelsPerElem = 3;
 | |
|       ret.pfn = &UniformAs3uiv;
 | |
|       break;
 | |
|     case LOCAL_GL_UNSIGNED_INT_VEC4:
 | |
|       ret.channelsPerElem = 4;
 | |
|       ret.pfn = &UniformAs4uiv;
 | |
|       break;
 | |
| 
 | |
|       // -
 | |
| 
 | |
|     case LOCAL_GL_FLOAT_MAT2:
 | |
|       ret.channelsPerElem = 2 * 2;
 | |
|       ret.pfn = &UniformAsMatrix2x2fv;
 | |
|       break;
 | |
|     case LOCAL_GL_FLOAT_MAT2x3:
 | |
|       ret.channelsPerElem = 2 * 3;
 | |
|       ret.pfn = &UniformAsMatrix2x3fv;
 | |
|       break;
 | |
|     case LOCAL_GL_FLOAT_MAT2x4:
 | |
|       ret.channelsPerElem = 2 * 4;
 | |
|       ret.pfn = &UniformAsMatrix2x4fv;
 | |
|       break;
 | |
| 
 | |
|     case LOCAL_GL_FLOAT_MAT3x2:
 | |
|       ret.channelsPerElem = 3 * 2;
 | |
|       ret.pfn = &UniformAsMatrix3x2fv;
 | |
|       break;
 | |
|     case LOCAL_GL_FLOAT_MAT3:
 | |
|       ret.channelsPerElem = 3 * 3;
 | |
|       ret.pfn = &UniformAsMatrix3x3fv;
 | |
|       break;
 | |
|     case LOCAL_GL_FLOAT_MAT3x4:
 | |
|       ret.channelsPerElem = 3 * 4;
 | |
|       ret.pfn = &UniformAsMatrix3x4fv;
 | |
|       break;
 | |
| 
 | |
|     case LOCAL_GL_FLOAT_MAT4x2:
 | |
|       ret.channelsPerElem = 4 * 2;
 | |
|       ret.pfn = &UniformAsMatrix4x2fv;
 | |
|       break;
 | |
|     case LOCAL_GL_FLOAT_MAT4x3:
 | |
|       ret.channelsPerElem = 4 * 3;
 | |
|       ret.pfn = &UniformAsMatrix4x3fv;
 | |
|       break;
 | |
|     case LOCAL_GL_FLOAT_MAT4:
 | |
|       ret.channelsPerElem = 4 * 4;
 | |
|       ret.pfn = &UniformAsMatrix4x4fv;
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       gfxCriticalError() << "Bad `elemType`: " << EnumString(info.elemType);
 | |
|       MOZ_CRASH("`elemType`");
 | |
|   }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| }  // namespace webgl
 | |
| 
 | |
| // -------------------------
 | |
| 
 | |
| //#define DUMP_SHADERVAR_MAPPINGS
 | |
| 
 | |
| RefPtr<const webgl::LinkedProgramInfo> QueryProgramInfo(WebGLProgram* prog,
 | |
|                                                         gl::GLContext* gl) {
 | |
|   WebGLContext* const webgl = prog->mContext;
 | |
| 
 | |
|   RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
 | |
| 
 | |
|   // Frag outputs
 | |
| 
 | |
|   {
 | |
|     const auto& fragShader = prog->FragShader();
 | |
|     const auto& compileResults = fragShader->CompileResults();
 | |
|     const auto version = compileResults->mShaderVersion;
 | |
| 
 | |
|     const auto fnAddInfo = [&](const webgl::FragOutputInfo& x) {
 | |
|       info->hasOutput[x.loc] = true;
 | |
|       info->fragOutputs.insert({x.loc, x});
 | |
|     };
 | |
| 
 | |
|     if (version == 300) {
 | |
|       for (const auto& cur : compileResults->mOutputVariables) {
 | |
|         auto loc = cur.location;
 | |
|         if (loc == -1) loc = 0;
 | |
| 
 | |
|         const auto info =
 | |
|             webgl::FragOutputInfo{uint8_t(loc), cur.name, cur.mappedName,
 | |
|                                   FragOutputBaseType(cur.type)};
 | |
|         if (!cur.isArray()) {
 | |
|           fnAddInfo(info);
 | |
|           continue;
 | |
|         }
 | |
|         MOZ_ASSERT(cur.arraySizes.size() == 1);
 | |
|         for (uint32_t i = 0; i < cur.arraySizes[0]; ++i) {
 | |
|           const auto indexStr = std::string("[") + std::to_string(i) + "]";
 | |
| 
 | |
|           const auto userName = info.userName + indexStr;
 | |
|           const auto mappedName = info.mappedName + indexStr;
 | |
| 
 | |
|           const auto indexedInfo = webgl::FragOutputInfo{
 | |
|               uint8_t(info.loc + i), userName, mappedName, info.baseType};
 | |
|           fnAddInfo(indexedInfo);
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       // ANGLE's translator doesn't tell us about non-user frag outputs. :(
 | |
| 
 | |
|       const auto& translatedSource = compileResults->mObjectCode;
 | |
|       uint32_t drawBuffers = 1;
 | |
|       if (translatedSource.find("(gl_FragData[1]") != std::string::npos ||
 | |
|           translatedSource.find("(webgl_FragData[1]") != std::string::npos) {
 | |
|         // The matching with the leading '(' prevents cleverly-named user vars
 | |
|         // breaking this. Since ANGLE initializes all outputs, if this is an MRT
 | |
|         // shader, FragData[1] will be present. FragData[0] is valid for non-MRT
 | |
|         // shaders.
 | |
|         drawBuffers = webgl->GLMaxDrawBuffers();
 | |
|       } else if (translatedSource.find("(gl_FragColor") == std::string::npos &&
 | |
|                  translatedSource.find("(webgl_FragColor") ==
 | |
|                      std::string::npos &&
 | |
|                  translatedSource.find("(gl_FragData") == std::string::npos &&
 | |
|                  translatedSource.find("(webgl_FragData") ==
 | |
|                      std::string::npos) {
 | |
|         // We have to support no-color-output shaders?
 | |
|         drawBuffers = 0;
 | |
|       }
 | |
| 
 | |
|       for (uint32_t i = 0; i < drawBuffers; ++i) {
 | |
|         const auto name = std::string("gl_FragData[") + std::to_string(i) + "]";
 | |
|         const auto info = webgl::FragOutputInfo{uint8_t(i), name, name,
 | |
|                                                 webgl::TextureBaseType::Float};
 | |
|         fnAddInfo(info);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const auto& vertShader = prog->VertShader();
 | |
|   const auto& vertCompileResults = vertShader->CompileResults();
 | |
|   const auto numViews = vertCompileResults->mVertexShaderNumViews;
 | |
|   if (numViews != -1) {
 | |
|     info->zLayerCount = AssertedCast<uint8_t>(numViews);
 | |
|   }
 | |
| 
 | |
|   // -
 | |
| 
 | |
|   auto& nameMap = info->nameMap;
 | |
| 
 | |
|   const auto fnAccum = [&](WebGLShader& shader) {
 | |
|     const auto& compRes = shader.CompileResults();
 | |
|     for (const auto& pair : compRes->mNameMap) {
 | |
|       nameMap.insert(pair);
 | |
|     }
 | |
|   };
 | |
|   fnAccum(*prog->VertShader());
 | |
|   fnAccum(*prog->FragShader());
 | |
| 
 | |
|   // -
 | |
| 
 | |
|   std::unordered_map<std::string, std::string> nameUnmap;
 | |
|   for (const auto& pair : nameMap) {
 | |
|     nameUnmap.insert({pair.second, pair.first});
 | |
|   }
 | |
| 
 | |
|   info->active =
 | |
|       GetLinkActiveInfo(*gl, prog->mGLName, webgl->IsWebGL2(), nameUnmap);
 | |
| 
 | |
|   // -
 | |
| 
 | |
|   for (const auto& attrib : info->active.activeAttribs) {
 | |
|     if (attrib.location == 0) {
 | |
|       info->attrib0Active = true;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // -
 | |
| 
 | |
|   for (const auto& uniform : info->active.activeUniforms) {
 | |
|     const auto& elemType = uniform.elemType;
 | |
|     webgl::SamplerUniformInfo* samplerInfo = nullptr;
 | |
|     const auto baseType = SamplerBaseType(elemType);
 | |
|     if (baseType) {
 | |
|       const bool isShadowSampler = IsShadowSampler(elemType);
 | |
| 
 | |
|       auto* texList = &webgl->mBound2DTextures;
 | |
| 
 | |
|       switch (elemType) {
 | |
|         case LOCAL_GL_SAMPLER_2D:
 | |
|         case LOCAL_GL_SAMPLER_2D_SHADOW:
 | |
|         case LOCAL_GL_INT_SAMPLER_2D:
 | |
|         case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
 | |
|           break;
 | |
| 
 | |
|         case LOCAL_GL_SAMPLER_CUBE:
 | |
|         case LOCAL_GL_SAMPLER_CUBE_SHADOW:
 | |
|         case LOCAL_GL_INT_SAMPLER_CUBE:
 | |
|         case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
 | |
|           texList = &webgl->mBoundCubeMapTextures;
 | |
|           break;
 | |
| 
 | |
|         case LOCAL_GL_SAMPLER_3D:
 | |
|         case LOCAL_GL_INT_SAMPLER_3D:
 | |
|         case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
 | |
|           texList = &webgl->mBound3DTextures;
 | |
|           break;
 | |
| 
 | |
|         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:
 | |
|           texList = &webgl->mBound2DArrayTextures;
 | |
|           break;
 | |
|       }
 | |
| 
 | |
|       auto curInfo = std::unique_ptr<webgl::SamplerUniformInfo>(
 | |
|           new webgl::SamplerUniformInfo{*texList, *baseType, isShadowSampler});
 | |
|       curInfo->texUnits.resize(uniform.elemCount);
 | |
|       samplerInfo = curInfo.get();
 | |
|       info->samplerUniforms.push_back(std::move(curInfo));
 | |
|     }
 | |
| 
 | |
|     const auto valInfo = webgl::ActiveUniformValidationInfo::Make(uniform);
 | |
| 
 | |
|     for (const auto& pair : uniform.locByIndex) {
 | |
|       info->locationMap.insert(
 | |
|           {pair.second, {valInfo, pair.first, samplerInfo}});
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // -
 | |
| 
 | |
|   {
 | |
|     const auto& activeBlocks = info->active.activeUniformBlocks;
 | |
|     info->uniformBlocks.reserve(activeBlocks.size());
 | |
|     for (const auto& cur : activeBlocks) {
 | |
|       const auto curInfo = webgl::UniformBlockInfo{
 | |
|           cur, &webgl->mIndexedUniformBufferBindings[0]};
 | |
|       info->uniformBlocks.push_back(curInfo);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return info;
 | |
| }
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
 | |
|     : prog(prog),
 | |
|       transformFeedbackBufferMode(prog->mNextLink_TransformFeedbackBufferMode) {
 | |
| }
 | |
| 
 | |
| webgl::LinkedProgramInfo::~LinkedProgramInfo() = default;
 | |
| 
 | |
| webgl::AttribBaseType webgl::ToAttribBaseType(const GLenum elemType) {
 | |
|   switch (elemType) {
 | |
|     case LOCAL_GL_BOOL:
 | |
|     case LOCAL_GL_BOOL_VEC2:
 | |
|     case LOCAL_GL_BOOL_VEC3:
 | |
|     case LOCAL_GL_BOOL_VEC4:
 | |
|       return webgl::AttribBaseType::Boolean;
 | |
| 
 | |
|     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_MAT3x2:
 | |
|     case LOCAL_GL_FLOAT_MAT2x4:
 | |
|     case LOCAL_GL_FLOAT_MAT4x2:
 | |
|     case LOCAL_GL_FLOAT_MAT3:
 | |
|     case LOCAL_GL_FLOAT_MAT3x4:
 | |
|     case LOCAL_GL_FLOAT_MAT4x3:
 | |
|     case LOCAL_GL_FLOAT_MAT4:
 | |
|       return webgl::AttribBaseType::Float;
 | |
| 
 | |
|     case LOCAL_GL_INT:
 | |
|     case LOCAL_GL_INT_VEC2:
 | |
|     case LOCAL_GL_INT_VEC3:
 | |
|     case LOCAL_GL_INT_VEC4:
 | |
|     case LOCAL_GL_SAMPLER_2D:
 | |
|     case LOCAL_GL_SAMPLER_3D:
 | |
|     case LOCAL_GL_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_SAMPLER_2D_SHADOW:
 | |
|     case LOCAL_GL_SAMPLER_2D_ARRAY:
 | |
|     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
 | |
|     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
 | |
|     case LOCAL_GL_INT_SAMPLER_2D:
 | |
|     case LOCAL_GL_INT_SAMPLER_3D:
 | |
|     case LOCAL_GL_INT_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
 | |
|       return webgl::AttribBaseType::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 webgl::AttribBaseType::Uint;
 | |
| 
 | |
|     default:
 | |
|       gfxCriticalError() << "Bad `elemType`: " << EnumString(elemType);
 | |
|       MOZ_CRASH("`elemType`");
 | |
|   }
 | |
| }
 | |
| 
 | |
| const char* webgl::ToString(const webgl::AttribBaseType x) {
 | |
|   switch (x) {
 | |
|     case webgl::AttribBaseType::Float:
 | |
|       return "FLOAT";
 | |
|     case webgl::AttribBaseType::Int:
 | |
|       return "INT";
 | |
|     case webgl::AttribBaseType::Uint:
 | |
|       return "UINT";
 | |
|     case webgl::AttribBaseType::Boolean:
 | |
|       return "BOOL";
 | |
|   }
 | |
|   MOZ_CRASH("pacify gcc6 warning");
 | |
| }
 | |
| 
 | |
| const char* webgl::ToString(const webgl::UniformBaseType x) {
 | |
|   switch (x) {
 | |
|     case webgl::UniformBaseType::Float:
 | |
|       return "FLOAT";
 | |
|     case webgl::UniformBaseType::Int:
 | |
|       return "INT";
 | |
|     case webgl::UniformBaseType::Uint:
 | |
|       return "UINT";
 | |
|   }
 | |
|   MOZ_CRASH("pacify gcc6 warning");
 | |
| }
 | |
| 
 | |
| const webgl::CachedDrawFetchLimits*
 | |
| webgl::LinkedProgramInfo::GetDrawFetchLimits() const {
 | |
|   const auto& webgl = prog->mContext;
 | |
|   const auto& vao = webgl->mBoundVertexArray;
 | |
| 
 | |
|   {
 | |
|     // We have to ensure that every enabled attrib array (not just the active
 | |
|     // ones) has a non-null buffer.
 | |
|     const auto badIndex = vao->GetAttribIsArrayWithNullBuffer();
 | |
|     if (badIndex) {
 | |
|       webgl->ErrorInvalidOperation(
 | |
|           "Vertex attrib array %u is enabled but"
 | |
|           " has no buffer bound.",
 | |
|           *badIndex);
 | |
|       return nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const auto& activeAttribs = active.activeAttribs;
 | |
| 
 | |
|   webgl::CachedDrawFetchLimits fetchLimits;
 | |
|   fetchLimits.usedBuffers =
 | |
|       std::move(mScratchFetchLimits.usedBuffers);  // Avoid realloc.
 | |
|   fetchLimits.usedBuffers.clear();
 | |
|   fetchLimits.usedBuffers.reserve(activeAttribs.size());
 | |
| 
 | |
|   bool hasActiveAttrib = false;
 | |
|   bool hasActiveDivisor0 = false;
 | |
| 
 | |
|   for (const auto& progAttrib : activeAttribs) {
 | |
|     const auto& loc = progAttrib.location;
 | |
|     if (loc == -1) continue;
 | |
|     hasActiveAttrib |= true;
 | |
| 
 | |
|     const auto& binding = vao->AttribBinding(loc);
 | |
|     const auto& buffer = binding.buffer;
 | |
|     const auto& layout = binding.layout;
 | |
|     hasActiveDivisor0 |= (layout.divisor == 0);
 | |
| 
 | |
|     webgl::AttribBaseType attribDataBaseType;
 | |
|     if (layout.isArray) {
 | |
|       MOZ_ASSERT(buffer);
 | |
|       fetchLimits.usedBuffers.push_back(
 | |
|           {buffer.get(), static_cast<uint32_t>(loc)});
 | |
| 
 | |
|       attribDataBaseType = layout.baseType;
 | |
| 
 | |
|       const auto availBytes = buffer->ByteLength();
 | |
|       const auto availElems = AvailGroups(availBytes, layout.byteOffset,
 | |
|                                           layout.byteSize, layout.byteStride);
 | |
|       if (layout.divisor) {
 | |
|         const auto availInstances =
 | |
|             CheckedInt<uint64_t>(availElems) * layout.divisor;
 | |
|         if (availInstances.isValid()) {
 | |
|           fetchLimits.maxInstances =
 | |
|               std::min(fetchLimits.maxInstances, availInstances.value());
 | |
|         }  // If not valid, it overflowed too large, so we're super safe.
 | |
|       } else {
 | |
|         fetchLimits.maxVerts = std::min(fetchLimits.maxVerts, availElems);
 | |
|       }
 | |
|     } else {
 | |
|       attribDataBaseType = webgl->mGenericVertexAttribTypes[loc];
 | |
|     }
 | |
| 
 | |
|     const auto& progBaseType = progAttrib.baseType;
 | |
|     if ((attribDataBaseType != progBaseType) &&
 | |
|         (progBaseType != webgl::AttribBaseType::Boolean)) {
 | |
|       const auto& dataType = ToString(attribDataBaseType);
 | |
|       const auto& progType = ToString(progBaseType);
 | |
|       webgl->ErrorInvalidOperation(
 | |
|           "Vertex attrib %u requires data of type %s,"
 | |
|           " but is being supplied with type %s.",
 | |
|           loc, progType, dataType);
 | |
|       return nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!webgl->IsWebGL2() && hasActiveAttrib && !hasActiveDivisor0) {
 | |
|     webgl->ErrorInvalidOperation(
 | |
|         "One active vertex attrib (if any are active)"
 | |
|         " must have a divisor of 0.");
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // -
 | |
| 
 | |
|   mScratchFetchLimits = std::move(fetchLimits);
 | |
|   return &mScratchFetchLimits;
 | |
| }
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////
 | |
| // WebGLProgram
 | |
| 
 | |
| WebGLProgram::WebGLProgram(WebGLContext* webgl)
 | |
|     : WebGLContextBoundObject(webgl),
 | |
|       mGLName(webgl->gl->fCreateProgram()),
 | |
|       mNumActiveTFOs(0),
 | |
|       mNextLink_TransformFeedbackBufferMode(LOCAL_GL_INTERLEAVED_ATTRIBS) {}
 | |
| 
 | |
| WebGLProgram::~WebGLProgram() {
 | |
|   mVertShader = nullptr;
 | |
|   mFragShader = nullptr;
 | |
| 
 | |
|   mMostRecentLinkInfo = nullptr;
 | |
| 
 | |
|   if (!mContext) return;
 | |
|   mContext->gl->fDeleteProgram(mGLName);
 | |
| }
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////
 | |
| // GL funcs
 | |
| 
 | |
| void WebGLProgram::AttachShader(WebGLShader& shader) {
 | |
|   RefPtr<WebGLShader>* shaderSlot = nullptr;
 | |
|   switch (shader.mType) {
 | |
|     case LOCAL_GL_VERTEX_SHADER:
 | |
|       shaderSlot = &mVertShader;
 | |
|       break;
 | |
|     case LOCAL_GL_FRAGMENT_SHADER:
 | |
|       shaderSlot = &mFragShader;
 | |
|       break;
 | |
|   }
 | |
|   MOZ_ASSERT(shaderSlot);
 | |
| 
 | |
|   *shaderSlot = &shader;
 | |
| 
 | |
|   mContext->gl->fAttachShader(mGLName, shader.mGLName);
 | |
| }
 | |
| 
 | |
| void WebGLProgram::BindAttribLocation(GLuint loc, const std::string& name) {
 | |
|   const auto err = CheckGLSLVariableName(mContext->IsWebGL2(), name);
 | |
|   if (err) {
 | |
|     mContext->GenerateError(err->type, "%s", err->info.c_str());
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (loc >= mContext->MaxVertexAttribs()) {
 | |
|     mContext->ErrorInvalidValue(
 | |
|         "`location` must be less than"
 | |
|         " MAX_VERTEX_ATTRIBS.");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (name.find("gl_") == 0) {
 | |
|     mContext->ErrorInvalidOperation(
 | |
|         "Can't set the location of a"
 | |
|         " name that starts with 'gl_'.");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto res = mNextLink_BoundAttribLocs.insert({name, loc});
 | |
| 
 | |
|   const auto& wasInserted = res.second;
 | |
|   if (!wasInserted) {
 | |
|     const auto& itr = res.first;
 | |
|     itr->second = loc;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void WebGLProgram::DetachShader(const WebGLShader& shader) {
 | |
|   RefPtr<WebGLShader>* shaderSlot = nullptr;
 | |
|   switch (shader.mType) {
 | |
|     case LOCAL_GL_VERTEX_SHADER:
 | |
|       shaderSlot = &mVertShader;
 | |
|       break;
 | |
|     case LOCAL_GL_FRAGMENT_SHADER:
 | |
|       shaderSlot = &mFragShader;
 | |
|       break;
 | |
|   }
 | |
|   MOZ_ASSERT(shaderSlot);
 | |
| 
 | |
|   if (*shaderSlot != &shader) return;
 | |
| 
 | |
|   *shaderSlot = nullptr;
 | |
| 
 | |
|   mContext->gl->fDetachShader(mGLName, shader.mGLName);
 | |
| }
 | |
| 
 | |
| void WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex,
 | |
|                                        GLuint uniformBlockBinding) const {
 | |
|   if (!IsLinked()) {
 | |
|     mContext->ErrorInvalidOperation("`program` must be linked.");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto& uniformBlocks = LinkInfo()->uniformBlocks;
 | |
|   if (uniformBlockIndex >= uniformBlocks.size()) {
 | |
|     mContext->ErrorInvalidValue("Index %u invalid.", uniformBlockIndex);
 | |
|     return;
 | |
|   }
 | |
|   auto& uniformBlock = uniformBlocks[uniformBlockIndex];
 | |
| 
 | |
|   const auto& indexedBindings = mContext->mIndexedUniformBufferBindings;
 | |
|   if (uniformBlockBinding >= indexedBindings.size()) {
 | |
|     mContext->ErrorInvalidValue("Binding %u invalid.", uniformBlockBinding);
 | |
|     return;
 | |
|   }
 | |
|   const auto& indexedBinding = indexedBindings[uniformBlockBinding];
 | |
| 
 | |
|   ////
 | |
| 
 | |
|   gl::GLContext* gl = mContext->GL();
 | |
|   gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
 | |
| 
 | |
|   ////
 | |
| 
 | |
|   uniformBlock.binding = &indexedBinding;
 | |
| }
 | |
| 
 | |
| bool WebGLProgram::ValidateForLink() {
 | |
|   const auto AppendCompileLog = [&](const WebGLShader* const shader) {
 | |
|     if (!shader) {
 | |
|       mLinkLog += " Missing shader.";
 | |
|       return;
 | |
|     }
 | |
|     mLinkLog += "\nSHADER_INFO_LOG:\n";
 | |
|     mLinkLog += shader->CompileLog();
 | |
|   };
 | |
| 
 | |
|   if (!mVertShader || !mVertShader->IsCompiled()) {
 | |
|     mLinkLog = "Must have a compiled vertex shader attached:";
 | |
|     AppendCompileLog(mVertShader);
 | |
|     return false;
 | |
|   }
 | |
|   const auto& vertInfo = *mVertShader->CompileResults();
 | |
| 
 | |
|   if (!mFragShader || !mFragShader->IsCompiled()) {
 | |
|     mLinkLog = "Must have a compiled fragment shader attached:";
 | |
|     AppendCompileLog(mFragShader);
 | |
|     return false;
 | |
|   }
 | |
|   const auto& fragInfo = *mFragShader->CompileResults();
 | |
| 
 | |
|   nsCString errInfo;
 | |
|   if (!fragInfo.CanLinkTo(vertInfo, &errInfo)) {
 | |
|     mLinkLog = errInfo.BeginReading();
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   const auto& gl = mContext->gl;
 | |
| 
 | |
|   if (gl->WorkAroundDriverBugs() && mContext->mIsMesa) {
 | |
|     // Bug 1203135: Mesa crashes internally if we exceed the reported maximum
 | |
|     // attribute count.
 | |
|     if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
 | |
|       mLinkLog =
 | |
|           "Number of attributes exceeds Mesa's reported max"
 | |
|           " attribute count.";
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void WebGLProgram::LinkProgram() {
 | |
|   if (mNumActiveTFOs) {
 | |
|     mContext->ErrorInvalidOperation(
 | |
|         "Program is in-use by one or more active"
 | |
|         " transform feedback objects.");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // as some of the validation changes program state
 | |
| 
 | |
|   mLinkLog = {};
 | |
|   mMostRecentLinkInfo = nullptr;
 | |
| 
 | |
|   if (!ValidateForLink()) {
 | |
|     mContext->GenerateWarning("%s", mLinkLog.c_str());
 | |
|     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) {
 | |
|     std::string postLinkLog;
 | |
|     if (ValidateAfterTentativeLink(&postLinkLog)) return;
 | |
| 
 | |
|     mMostRecentLinkInfo = nullptr;
 | |
|     mLinkLog = std::move(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.empty()) {
 | |
|       mContext->GenerateWarning(
 | |
|           "Failed to link, leaving the following"
 | |
|           " log:\n%s\n",
 | |
|           mLinkLog.c_str());
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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;
 | |
|   }
 | |
| }
 | |
| 
 | |
| uint8_t ElemTypeComponents(const GLenum elemType) {
 | |
|   switch (elemType) {
 | |
|     case LOCAL_GL_BOOL:
 | |
|     case LOCAL_GL_FLOAT:
 | |
|     case LOCAL_GL_INT:
 | |
|     case LOCAL_GL_UNSIGNED_INT:
 | |
|     case LOCAL_GL_SAMPLER_2D:
 | |
|     case LOCAL_GL_SAMPLER_3D:
 | |
|     case LOCAL_GL_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_SAMPLER_2D_SHADOW:
 | |
|     case LOCAL_GL_SAMPLER_2D_ARRAY:
 | |
|     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
 | |
|     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
 | |
|     case LOCAL_GL_INT_SAMPLER_2D:
 | |
|     case LOCAL_GL_INT_SAMPLER_3D:
 | |
|     case LOCAL_GL_INT_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
 | |
|     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
 | |
|       return 1;
 | |
| 
 | |
|     case LOCAL_GL_BOOL_VEC2:
 | |
|     case LOCAL_GL_FLOAT_VEC2:
 | |
|     case LOCAL_GL_INT_VEC2:
 | |
|     case LOCAL_GL_UNSIGNED_INT_VEC2:
 | |
|       return 2;
 | |
| 
 | |
|     case LOCAL_GL_BOOL_VEC3:
 | |
|     case LOCAL_GL_FLOAT_VEC3:
 | |
|     case LOCAL_GL_INT_VEC3:
 | |
|     case LOCAL_GL_UNSIGNED_INT_VEC3:
 | |
|       return 3;
 | |
| 
 | |
|     case LOCAL_GL_BOOL_VEC4:
 | |
|     case LOCAL_GL_FLOAT_VEC4:
 | |
|     case LOCAL_GL_INT_VEC4:
 | |
|     case LOCAL_GL_UNSIGNED_INT_VEC4:
 | |
|     case LOCAL_GL_FLOAT_MAT2:
 | |
|       return 4;
 | |
| 
 | |
|     case LOCAL_GL_FLOAT_MAT2x3:
 | |
|     case LOCAL_GL_FLOAT_MAT3x2:
 | |
|       return 2 * 3;
 | |
| 
 | |
|     case LOCAL_GL_FLOAT_MAT2x4:
 | |
|     case LOCAL_GL_FLOAT_MAT4x2:
 | |
|       return 2 * 4;
 | |
| 
 | |
|     case LOCAL_GL_FLOAT_MAT3:
 | |
|       return 3 * 3;
 | |
| 
 | |
|     case LOCAL_GL_FLOAT_MAT3x4:
 | |
|     case LOCAL_GL_FLOAT_MAT4x3:
 | |
|       return 3 * 4;
 | |
| 
 | |
|     case LOCAL_GL_FLOAT_MAT4:
 | |
|       return 4 * 4;
 | |
| 
 | |
|     default:
 | |
|       return 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool WebGLProgram::ValidateAfterTentativeLink(
 | |
|     std::string* const out_linkLog) const {
 | |
|   const auto& linkInfo = mMostRecentLinkInfo;
 | |
|   const auto& gl = mContext->gl;
 | |
| 
 | |
|   // Check for overlapping attrib locations.
 | |
|   {
 | |
|     std::unordered_map<uint32_t, const std::string&> nameByLoc;
 | |
|     for (const auto& attrib : linkInfo->active.activeAttribs) {
 | |
|       if (attrib.location == -1) continue;
 | |
| 
 | |
|       const auto& elemType = attrib.elemType;
 | |
|       const auto numUsedLocs = NumUsedLocationsByElemType(elemType);
 | |
|       for (uint32_t i = 0; i < numUsedLocs; i++) {
 | |
|         const uint32_t usedLoc = attrib.location + i;
 | |
| 
 | |
|         const auto res = nameByLoc.insert({usedLoc, attrib.name});
 | |
|         const bool& didInsert = res.second;
 | |
|         if (!didInsert) {
 | |
|           const auto& aliasingName = attrib.name;
 | |
|           const auto& itrExisting = res.first;
 | |
|           const auto& existingName = itrExisting->second;
 | |
|           *out_linkLog = nsPrintfCString(
 | |
|                              "Attrib \"%s\" aliases locations used by"
 | |
|                              " attrib \"%s\".",
 | |
|                              aliasingName.c_str(), existingName.c_str())
 | |
|                              .BeginReading();
 | |
|           return false;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Forbid too many components for specified buffer mode
 | |
|   const auto& activeTfVaryings = linkInfo->active.activeTfVaryings;
 | |
|   MOZ_ASSERT(mNextLink_TransformFeedbackVaryings.size() ==
 | |
|              activeTfVaryings.size());
 | |
|   if (!activeTfVaryings.empty()) {
 | |
|     GLuint maxComponentsPerIndex = 0;
 | |
|     switch (linkInfo->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;
 | |
|     for (const auto& cur : activeTfVaryings) {
 | |
|       if (componentsPerVert.empty() ||
 | |
|           linkInfo->transformFeedbackBufferMode == LOCAL_GL_SEPARATE_ATTRIBS) {
 | |
|         componentsPerVert.push_back(0);
 | |
|       }
 | |
| 
 | |
|       size_t varyingComponents = ElemTypeComponents(cur.elemType);
 | |
|       MOZ_ASSERT(varyingComponents);
 | |
|       varyingComponents *= cur.elemCount;
 | |
| 
 | |
|       auto& totalComponentsForIndex = *(componentsPerVert.rbegin());
 | |
|       totalComponentsForIndex += varyingComponents;
 | |
| 
 | |
|       if (totalComponentsForIndex > maxComponentsPerIndex) {
 | |
|         *out_linkLog = nsPrintfCString(
 | |
|                            "Transform feedback varying \"%s\""
 | |
|                            " pushed `componentsForIndex` over the"
 | |
|                            " limit of %u.",
 | |
|                            cur.name.c_str(), maxComponentsPerIndex)
 | |
|                            .BeginReading();
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     linkInfo->componentsPerTFVert = std::move(componentsPerVert);
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool WebGLProgram::UseProgram() const {
 | |
|   if (!mMostRecentLinkInfo) {
 | |
|     mContext->ErrorInvalidOperation(
 | |
|         "Program has not been successfully linked.");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (mContext->mBoundTransformFeedback &&
 | |
|       mContext->mBoundTransformFeedback->mIsActive &&
 | |
|       !mContext->mBoundTransformFeedback->mIsPaused) {
 | |
|     mContext->ErrorInvalidOperation(
 | |
|         "Transform feedback active and not paused.");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   mContext->gl->fUseProgram(mGLName);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool WebGLProgram::ValidateProgram() const {
 | |
|   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(
 | |
|         "Implemented as a no-op on"
 | |
|         " Mac to work around crashes.");
 | |
|     return true;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   gl->fValidateProgram(mGLName);
 | |
|   GLint ok = 0;
 | |
|   gl->fGetProgramiv(mGLName, LOCAL_GL_VALIDATE_STATUS, &ok);
 | |
|   return bool(ok);
 | |
| }
 | |
| 
 | |
| ////////////////////////////////////////////////////////////////////////////////
 | |
| 
 | |
| 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) {
 | |
|       std::vector<char> buffer(logLenWithNull);
 | |
|       gl->fGetProgramInfoLog(mGLName, buffer.size(), nullptr, buffer.data());
 | |
|       mLinkLog = buffer.data();
 | |
|     } else {
 | |
|       mLinkLog.clear();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   GLint ok = 0;
 | |
|   gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
 | |
|   if (!ok) return;
 | |
| 
 | |
|   mMostRecentLinkInfo =
 | |
|       QueryProgramInfo(this, gl);  // Fallible after context loss.
 | |
| }
 | |
| 
 | |
| void WebGLProgram::TransformFeedbackVaryings(
 | |
|     const std::vector<std::string>& varyings, GLenum bufferMode) {
 | |
|   const auto& gl = mContext->gl;
 | |
| 
 | |
|   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.size() > maxAttribs) {
 | |
|         mContext->ErrorInvalidValue("Length of `varyings` exceeds %s.",
 | |
|                                     "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
 | |
|         return;
 | |
|       }
 | |
|     } break;
 | |
| 
 | |
|     default:
 | |
|       mContext->ErrorInvalidEnumInfo("bufferMode", bufferMode);
 | |
|       return;
 | |
|   }
 | |
| 
 | |
|   ////
 | |
| 
 | |
|   mNextLink_TransformFeedbackVaryings = varyings;
 | |
|   mNextLink_TransformFeedbackBufferMode = bufferMode;
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla
 | 
