Add workaround for unused std140 and shared uniform blocks on MacOS

On some Mac drivers with shader version 4.1, they will
treat unused std140 and shared uniform blocks' members as inactive. However,
WebGL2.0 based on OpenGL ES3.0.4 requires all members of a named uniform block
declared with a shared or std140 layout qualifier to be considered active.
The uniform block itself is also considered active.

This workaround is to reference all members of unused std140 and shared uniform blocks
at the beginning of the vertex/fragment shader's main().

BUG=chromium:618464
TEST=UniformBufferTest.ActiveUniformBlockNumber

Change-Id: I1d2c5e3e8da04786ac6a37fd26f7bb9c14cd76ed
Reviewed-on: https://chromium-review.googlesource.com/387169
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Qin Jiajia 2016-09-29 08:42:42 +08:00 коммит произвёл Commit Bot
Родитель 886de369c7
Коммит 9aa83fe302
13 изменённых файлов: 436 добавлений и 2 удалений

Просмотреть файл

@ -64,6 +64,10 @@ Intel Corporation
Andy Chen
Josh Triplett
Sudarsana Nagineni
Jiajia Qin
Jiawei Shao
Jie Chen
Qiankun Miao
Klarälvdalens Datakonsult AB
Milian Wolff

Просмотреть файл

@ -49,7 +49,7 @@ typedef unsigned int GLenum;
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 162
#define ANGLE_SH_VERSION 163
typedef enum {
SH_GLES2_SPEC,
@ -208,6 +208,14 @@ const ShCompileOptions SH_REWRITE_INTEGER_UNARY_MINUS_OPERATOR = UINT64_C(1) <<
// It works by using an expression to emulate this function.
const ShCompileOptions SH_EMULATE_ISNAN_FLOAT_FUNCTION = UINT64_C(1) << 27;
// This flag will use all uniforms of unused std140 and shared uniform blocks at the
// beginning of the vertex/fragment shader's main(). It is intended as a workaround for Mac
// drivers with shader version 4.10. In those drivers, they will treat unused
// std140 and shared uniform blocks' members as inactive. However, WebGL2.0 based on
// OpenGL ES3.0.4 requires all members of a named uniform block declared with a shared or std140
// layout qualifier to be considered active. The uniform block itself is also considered active.
const ShCompileOptions SH_USE_UNUSED_STANDARD_SHARED_BLOCKS = UINT64_C(1) << 28;
// Defines alternate strategies for implementing array index clamping.
typedef enum {
// Use the clamp intrinsic for array index clamping.

Просмотреть файл

@ -110,6 +110,8 @@
'compiler/translator/Types.h',
'compiler/translator/UnfoldShortCircuitAST.cpp',
'compiler/translator/UnfoldShortCircuitAST.h',
'compiler/translator/UseInterfaceBlockFields.cpp',
'compiler/translator/UseInterfaceBlockFields.h',
'compiler/translator/ValidateGlobalInitializer.cpp',
'compiler/translator/ValidateGlobalInitializer.h',
'compiler/translator/ValidateLimitations.cpp',

Просмотреть файл

@ -26,6 +26,7 @@
#include "compiler/translator/RewriteDoWhile.h"
#include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
#include "compiler/translator/UnfoldShortCircuitAST.h"
#include "compiler/translator/UseInterfaceBlockFields.h"
#include "compiler/translator/ValidateLimitations.h"
#include "compiler/translator/ValidateMaxParameters.h"
#include "compiler/translator/ValidateOutputs.h"
@ -391,6 +392,10 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[],
if (success && shouldCollectVariables(compileOptions))
{
collectVariables(root);
if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
{
useAllMembersInUnusedStandardAndSharedBlocks(root);
}
if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS)
{
success = enforcePackingRestrictions();
@ -853,6 +858,22 @@ void TCompiler::initializeGLPosition(TIntermNode* root)
InitializeVariables(root, list);
}
void TCompiler::useAllMembersInUnusedStandardAndSharedBlocks(TIntermNode *root)
{
sh::InterfaceBlockList list;
for (auto block : interfaceBlocks)
{
if (!block.staticUse &&
(block.layout == sh::BLOCKLAYOUT_STANDARD || block.layout == sh::BLOCKLAYOUT_SHARED))
{
list.push_back(block);
}
}
sh::UseInterfaceBlockFields(root, list);
}
void TCompiler::initializeOutputVariables(TIntermNode *root)
{
InitVariableList list;

Просмотреть файл

@ -132,6 +132,10 @@ class TCompiler : public TShHandleBase
// Returns true if, after applying the packing rules in the GLSL 1.017 spec
// Appendix A, section 7, the shader does not use too many uniforms.
bool enforcePackingRestrictions();
// Insert statements to reference all members in unused uniform blocks with standard and shared
// layout. This is to work around a Mac driver that treats unused standard/shared
// uniform blocks as inactive.
void useAllMembersInUnusedStandardAndSharedBlocks(TIntermNode *root);
// Insert statements to initialize output variables in the beginning of main().
// This is to avoid undefined behaviors.
void initializeOutputVariables(TIntermNode *root);

Просмотреть файл

@ -0,0 +1,176 @@
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// UseInterfaceBlockFields.cpp: insert statements to reference all members in InterfaceBlock list at
// the beginning of main. This is to work around a Mac driver that treats unused standard/shared
// uniform blocks as inactive.
#include "compiler/translator/UseInterfaceBlockFields.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/util.h"
namespace sh
{
namespace
{
class UseUniformBlockMembers : public TIntermTraverser
{
public:
UseUniformBlockMembers(const InterfaceBlockList &blocks)
: TIntermTraverser(true, false, false), mBlocks(blocks), mCodeInserted(false)
{
}
protected:
bool visitAggregate(Visit visit, TIntermAggregate *node) override;
private:
void insertUseCode(TIntermSequence *sequence);
void AddFieldUseStatements(const ShaderVariable &var, TIntermSequence *sequence);
const InterfaceBlockList &mBlocks;
bool mCodeInserted;
};
bool UseUniformBlockMembers::visitAggregate(Visit visit, TIntermAggregate *node)
{
bool visitChildren = !mCodeInserted;
switch (node->getOp())
{
case EOpSequence:
break;
case EOpFunction:
{
ASSERT(visit == PreVisit);
if (node->getName() == "main(")
{
TIntermSequence *sequence = node->getSequence();
ASSERT((sequence->size() == 1) || (sequence->size() == 2));
TIntermAggregate *body = nullptr;
if (sequence->size() == 1)
{
body = new TIntermAggregate(EOpSequence);
sequence->push_back(body);
}
else
{
body = (*sequence)[1]->getAsAggregate();
}
ASSERT(body);
insertUseCode(body->getSequence());
mCodeInserted = true;
visitChildren = false;
}
break;
}
default:
visitChildren = false;
break;
}
return visitChildren;
}
void UseUniformBlockMembers::AddFieldUseStatements(const ShaderVariable &var,
TIntermSequence *sequence)
{
TString name = TString(var.name.c_str());
TType type = GetShaderVariableType(var);
if (var.isArray())
{
size_t pos = name.find_last_of('[');
if (pos != TString::npos)
{
name = name.substr(0, pos);
}
TType elementType = type;
elementType.clearArrayness();
TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, type);
for (unsigned int i = 0; i < var.arraySize; ++i)
{
TIntermBinary *element =
new TIntermBinary(EOpIndexDirect, arraySymbol, TIntermTyped::CreateIndexNode(i));
sequence->insert(sequence->begin(), element);
}
}
else if (var.isStruct())
{
TIntermSymbol *structSymbol = new TIntermSymbol(0, name, type);
for (unsigned int i = 0; i < var.fields.size(); ++i)
{
TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct, structSymbol,
TIntermTyped::CreateIndexNode(i));
sequence->insert(sequence->begin(), element);
}
}
else
{
TIntermSymbol *symbol = new TIntermSymbol(0, name, type);
sequence->insert(sequence->begin(), symbol);
}
}
void UseUniformBlockMembers::insertUseCode(TIntermSequence *sequence)
{
for (const auto &block : mBlocks)
{
if (block.instanceName.empty())
{
for (const auto &var : block.fields)
{
AddFieldUseStatements(var, sequence);
}
}
else if (block.arraySize > 0)
{
TType type = GetInterfaceBlockType(block);
TString name = TString(block.instanceName.c_str());
TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, type);
for (unsigned int i = 0; i < block.arraySize; ++i)
{
TIntermBinary *instanceSymbol = new TIntermBinary(EOpIndexDirect, arraySymbol,
TIntermTyped::CreateIndexNode(i));
for (unsigned int j = 0; j < block.fields.size(); ++j)
{
TIntermBinary *element =
new TIntermBinary(EOpIndexDirectInterfaceBlock, instanceSymbol,
TIntermTyped::CreateIndexNode(j));
sequence->insert(sequence->begin(), element);
}
}
}
else
{
TType type = GetInterfaceBlockType(block);
TString name = TString(block.instanceName.c_str());
TIntermSymbol *blockSymbol = new TIntermSymbol(0, name, type);
for (unsigned int i = 0; i < block.fields.size(); ++i)
{
TIntermBinary *element = new TIntermBinary(
EOpIndexDirectInterfaceBlock, blockSymbol, TIntermTyped::CreateIndexNode(i));
sequence->insert(sequence->begin(), element);
}
}
}
}
} // namespace anonymous
void UseInterfaceBlockFields(TIntermNode *root, const InterfaceBlockList &blocks)
{
UseUniformBlockMembers useUniformBlock(blocks);
root->traverse(&useUniformBlock);
}
} // namespace sh

Просмотреть файл

@ -0,0 +1,25 @@
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// UseInterfaceBlockFields.h: insert statements to reference all members in InterfaceBlock list at
// the beginning of main. This is to work around a Mac driver that treats unused standard/shared
// uniform blocks as inactive.
#ifndef COMPILER_TRANSLATOR_USEINTERFACEBLOCKFIELDS_H_
#define COMPILER_TRANSLATOR_USEINTERFACEBLOCKFIELDS_H_
#include <GLSLANG/ShaderLang.h>
class TIntermNode;
namespace sh
{
using InterfaceBlockList = std::vector<sh::InterfaceBlock>;
void UseInterfaceBlockFields(TIntermNode *root, const InterfaceBlockList &blocks);
} // namespace sh
#endif // COMPILER_TRANSLATOR_USEINTERFACEBLOCKFIELDS_H_

Просмотреть файл

@ -278,10 +278,38 @@ InterpolationType GetInterpolationType(TQualifier qualifier)
}
}
TType GetInterfaceBlockType(const sh::InterfaceBlock &block)
{
TType type;
TFieldList *fields = new TFieldList;
TSourceLoc loc;
for (const auto &field : block.fields)
{
TType *fieldType = new TType(GetShaderVariableType(field));
fields->push_back(new TField(fieldType, new TString(field.name.c_str()), loc));
}
TInterfaceBlock *interfaceBlock = new TInterfaceBlock(
new TString(block.name.c_str()), fields, new TString(block.instanceName.c_str()),
block.arraySize, TLayoutQualifier::create());
type.setBasicType(EbtInterfaceBlock);
type.setInterfaceBlock(interfaceBlock);
return type;
}
TType GetShaderVariableBasicType(const sh::ShaderVariable &var)
{
switch (var.type)
{
case GL_BOOL:
return TType(EbtBool);
case GL_BOOL_VEC2:
return TType(EbtBool, 2);
case GL_BOOL_VEC3:
return TType(EbtBool, 3);
case GL_BOOL_VEC4:
return TType(EbtBool, 4);
case GL_FLOAT:
return TType(EbtFloat);
case GL_FLOAT_VEC2:

Просмотреть файл

@ -38,6 +38,7 @@ bool IsVarying(TQualifier qualifier);
InterpolationType GetInterpolationType(TQualifier qualifier);
TString ArrayString(const TType &type);
TType GetInterfaceBlockType(const sh::InterfaceBlock &block);
TType GetShaderVariableBasicType(const sh::ShaderVariable &var);
TType GetShaderVariableType(const sh::ShaderVariable &var);

Просмотреть файл

@ -70,6 +70,11 @@ ShCompileOptions ShaderGL::prepareSourceAndReturnOptions(std::stringstream *sour
options |= SH_EMULATE_ISNAN_FLOAT_FUNCTION;
}
if (mWorkarounds.useUnusedBlocksWithStandardOrSharedLayout)
{
options |= SH_USE_UNUSED_STANDARD_SHARED_BLOCKS;
}
return options;
}

Просмотреть файл

@ -24,7 +24,8 @@ struct WorkaroundsGL
unpackOverlappingRowsSeparatelyUnpackBuffer(false),
emulateAbsIntFunction(false),
addAndTrueToLoopCondition(false),
emulateIsnanFloat(false)
emulateIsnanFloat(false),
useUnusedBlocksWithStandardOrSharedLayout(false)
{
}
@ -105,6 +106,11 @@ struct WorkaroundsGL
// this bug, we use an expression to emulate function isnan().
// Tracking bug: http://crbug.com/650547
bool emulateIsnanFloat;
// On Mac with OpenGL version 4.1, unused std140 or shared uniform blocks will be
// treated as inactive which is not consistent with WebGL2.0 spec. Reference all members in a
// unused std140 or shared uniform block at the beginning of main to work around it.
bool useUnusedBlocksWithStandardOrSharedLayout;
};
}

Просмотреть файл

@ -897,6 +897,7 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround
#if defined(ANGLE_PLATFORM_APPLE)
workarounds->doWhileGLSLCausesGPUHang = true;
workarounds->useUnusedBlocksWithStandardOrSharedLayout = true;
#endif
workarounds->finishDoesNotCauseQueriesToBeAvailable =

Просмотреть файл

@ -5,6 +5,7 @@
//
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
@ -396,6 +397,158 @@ TEST_P(UniformBufferTest, ActiveUniformNames)
EXPECT_EQ("blockName.f", std::string(&strBuffer[0]));
}
// Tests active uniforms and blocks when the layout is std140, shared and packed.
TEST_P(UniformBufferTest, ActiveUniformNumberAndName)
{
// TODO(Jiajia): Figure out why this fails on Intel on Mac.
// This case can pass on Intel Mac-10.11/10.12. But it fails on Intel Mac-10.10.
if (IsIntel() && IsOSX())
{
std::cout << "Test skipped on Intel on Mac." << std::endl;
return;
}
if (IsAMD() && IsWindows())
{
std::cout << "Test skipped on AMD on Win." << std::endl;
return;
}
const std::string &vertexShaderSource =
"#version 300 es\n"
"in vec2 position;\n"
"out float v;\n"
"struct S {\n"
" highp ivec3 a;\n"
" mediump ivec2 b[4];\n"
"};\n"
"struct T {\n"
" S c[2];\n"
"};\n"
"layout(std140) uniform blockName0 {\n"
" S s0;\n"
" lowp vec2 v0;\n"
" T t0[2];\n"
" highp uint u0;\n"
"};\n"
"layout(std140) uniform blockName1 {\n"
" float f1;\n"
" bool b1;\n"
"} instanceName1;\n"
"layout(shared) uniform blockName2 {\n"
" float f2;\n"
"};\n"
"layout(packed) uniform blockName3 {\n"
" float f3;\n"
"};\n"
"void main() {\n"
" v = instanceName1.f1;\n"
" gl_Position = vec4(position, 0, 1);\n"
"}";
const std::string &fragmentShaderSource =
"#version 300 es\n"
"precision highp float;\n"
"in float v;\n"
"out vec4 color;\n"
"void main() {\n"
" color = vec4(v, 0, 0, 1);\n"
"}";
ANGLE_GL_PROGRAM(program, vertexShaderSource, fragmentShaderSource);
GLint activeUniforms;
glGetProgramiv(program.get(), GL_ACTIVE_UNIFORMS, &activeUniforms);
ASSERT_EQ(15, activeUniforms);
GLint activeUniformBlocks;
glGetProgramiv(program.get(), GL_ACTIVE_UNIFORM_BLOCKS, &activeUniformBlocks);
ASSERT_EQ(3, activeUniformBlocks);
GLint maxLength, size;
GLenum type;
GLsizei length;
glGetProgramiv(program.get(), GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength);
std::vector<GLchar> strBuffer(maxLength + 1, 0);
glGetActiveUniform(program.get(), 0, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(1, size);
EXPECT_EQ("s0.a", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 1, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(4, size);
EXPECT_EQ("s0.b[0]", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 2, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(1, size);
EXPECT_EQ("v0", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 3, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(1, size);
EXPECT_EQ("t0[0].c[0].a", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 4, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(4, size);
EXPECT_EQ("t0[0].c[0].b[0]", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 5, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(1, size);
EXPECT_EQ("t0[0].c[1].a", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 6, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(4, size);
EXPECT_EQ("t0[0].c[1].b[0]", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 7, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(1, size);
EXPECT_EQ("t0[1].c[0].a", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 8, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(4, size);
EXPECT_EQ("t0[1].c[0].b[0]", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 9, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(1, size);
EXPECT_EQ("t0[1].c[1].a", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 10, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(4, size);
EXPECT_EQ("t0[1].c[1].b[0]", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 11, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(1, size);
EXPECT_EQ("u0", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 12, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(1, size);
EXPECT_EQ("blockName1.f1", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 13, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(1, size);
EXPECT_EQ("blockName1.b1", std::string(&strBuffer[0]));
glGetActiveUniform(program.get(), 14, maxLength, &length, &size, &type, &strBuffer[0]);
ASSERT_GL_NO_ERROR();
EXPECT_EQ(1, size);
EXPECT_EQ("f2", std::string(&strBuffer[0]));
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(UniformBufferTest,
ES3_D3D11(),