We must not check frag output type agreement if colorMask(0,0,0,0). We must allow drawing with no frag outputs, leaving color values unchanged. (evidently this is used enough that we spec'd it this way?) Differential Revision: https://phabricator.services.mozilla.com/D63287
1242 lines
37 KiB
C++
1242 lines
37 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/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->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;
|
|
|
|
const auto found = mDrawFetchCache.Find(vao);
|
|
if (found) return found;
|
|
|
|
const auto& activeAttribs = active.activeAttribs;
|
|
|
|
webgl::CachedDrawFetchLimits fetchLimits;
|
|
fetchLimits.usedBuffers.reserve(activeAttribs.size());
|
|
|
|
std::vector<const CacheInvalidator*> cacheDeps;
|
|
cacheDeps.reserve(2 + activeAttribs.size());
|
|
cacheDeps.push_back(vao.get());
|
|
cacheDeps.push_back(&webgl->mGenericVertexAttribTypeInvalidator);
|
|
|
|
{
|
|
// We have to ensure that every enabled attrib array (not just the active
|
|
// ones) has a non-null buffer.
|
|
bool err = false;
|
|
for (const auto& cur : vao->mAttribs) {
|
|
err |= (cur.mEnabled && !cur.mBuf);
|
|
}
|
|
if (MOZ_UNLIKELY(err)) {
|
|
uint32_t i = 0;
|
|
for (const auto& cur : vao->mAttribs) {
|
|
if (cur.mEnabled && !cur.mBuf) {
|
|
webgl->ErrorInvalidOperation(
|
|
"Vertex attrib array %u is enabled but"
|
|
" has no buffer bound.",
|
|
i);
|
|
return nullptr;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool hasActiveAttrib = false;
|
|
bool hasActiveDivisor0 = false;
|
|
|
|
for (const auto& progAttrib : activeAttribs) {
|
|
const auto& loc = progAttrib.location;
|
|
if (loc == -1) continue;
|
|
hasActiveAttrib |= true;
|
|
|
|
const auto& attribData = vao->mAttribs[loc];
|
|
hasActiveDivisor0 |= (attribData.mDivisor == 0);
|
|
|
|
webgl::AttribBaseType attribDataBaseType;
|
|
if (attribData.mEnabled) {
|
|
MOZ_ASSERT(attribData.mBuf);
|
|
fetchLimits.usedBuffers.push_back(
|
|
{attribData.mBuf.get(), static_cast<uint32_t>(loc)});
|
|
|
|
cacheDeps.push_back(&attribData.mBuf->mFetchInvalidator);
|
|
|
|
attribDataBaseType = attribData.BaseType();
|
|
|
|
const size_t availBytes = attribData.mBuf->ByteLength();
|
|
const auto availElems =
|
|
AvailGroups(availBytes, attribData.ByteOffset(),
|
|
attribData.BytesPerVertex(), attribData.ExplicitStride());
|
|
if (attribData.mDivisor) {
|
|
const auto availInstances =
|
|
CheckedInt<uint64_t>(availElems) * attribData.mDivisor;
|
|
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 = ToAttribBaseType(progAttrib.elemType);
|
|
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 (hasActiveAttrib && !hasActiveDivisor0) {
|
|
webgl->ErrorInvalidOperation(
|
|
"One active vertex attrib (if any are active)"
|
|
" must have a divisor of 0.");
|
|
return nullptr;
|
|
}
|
|
|
|
// -
|
|
|
|
if (!vao->ShouldCache()) {
|
|
mScratchFetchLimits = std::move(fetchLimits);
|
|
return &mScratchFetchLimits;
|
|
}
|
|
|
|
// -
|
|
|
|
auto entry = mDrawFetchCache.MakeEntry(vao.get(), std::move(fetchLimits));
|
|
entry->ResetInvalidators(std::move(cacheDeps));
|
|
return mDrawFetchCache.Insert(std::move(entry));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 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() {
|
|
if (!mVertShader || !mVertShader->IsCompiled()) {
|
|
mLinkLog = "Must have a compiled vertex shader attached.";
|
|
return false;
|
|
}
|
|
const auto& vertInfo = *mVertShader->CompileResults();
|
|
|
|
if (!mFragShader || !mFragShader->IsCompiled()) {
|
|
mLinkLog = "Must have an compiled fragment shader attached.";
|
|
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 if the attrib name conflicting to uniform name
|
|
{
|
|
std::unordered_set<std::string> attribNames;
|
|
for (const auto& attrib : linkInfo->active.activeAttribs) {
|
|
attribNames.insert(attrib.name);
|
|
}
|
|
for (const auto& uniform : linkInfo->active.activeUniforms) {
|
|
auto name = uniform.name;
|
|
const auto maybe = webgl::ParseIndexed(name);
|
|
if (maybe) {
|
|
name = maybe->name;
|
|
}
|
|
if (attribNames.count(name)) {
|
|
*out_linkLog = nsPrintfCString(
|
|
"Attrib name conflicts with uniform name:"
|
|
" %s",
|
|
name.c_str())
|
|
.BeginReading();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
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
|