зеркало из https://github.com/mozilla/gecko-dev.git
1220 строки
37 KiB
C++
1220 строки
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/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->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() {
|
|
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
|