Commit 9696316d by Olli Etuaho Committed by Commit Bot

Support ESSL structs containing samplers on D3D

Since HLSL can't natively handle samplers in structs, samplers need to be extracted out of structs into separate variables in the translated shader code. In HLSL 4.1, samplers that were in structs go into the normal sampler arrays and are identified by index constants. In other HLSL versions, samplers that were in structs are translated as uniform variables. These transformations are done inside the HLSL output classes, not as tree transformations. This helps to keep the uniform API provided by the shader translator intact. Wherever a struct containing samplers is passed into a user-defined function, the translated HLSL code passes the separate sampler variables alongside a struct where the samplers have been removed. The D3D backend in libANGLE queries the uniform registers of any samplers that were in uniform structs, and adds them to the register maps, so that correct sampler state gets assigned to them. The extracted sampler variables are prefixed with "angle_" instead of the usual "_" to prevent any name conflicts between them and regular variables. BUG=angleproject:504 TEST=angle_end2end_tests, dEQP-GLES*.functional.shaders.struct.uniform.* (all pass), dEQP-GLES*.functional.uniform_api.* (most now pass) Change-Id: Ib79cba2fa0ff8257a973d70dfd917a64f0ca1efb Reviewed-on: https://chromium-review.googlesource.com/333743Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent f0fee07a
...@@ -48,7 +48,7 @@ typedef unsigned int GLenum; ...@@ -48,7 +48,7 @@ typedef unsigned int GLenum;
// Version number for shader translation API. // Version number for shader translation API.
// It is incremented every time the API changes. // It is incremented every time the API changes.
#define ANGLE_SH_VERSION 144 #define ANGLE_SH_VERSION 145
typedef enum { typedef enum {
SH_GLES2_SPEC = 0x8B40, SH_GLES2_SPEC = 0x8B40,
...@@ -454,16 +454,10 @@ COMPILER_EXPORT bool ShGetInterfaceBlockRegister(const ShHandle handle, ...@@ -454,16 +454,10 @@ COMPILER_EXPORT bool ShGetInterfaceBlockRegister(const ShHandle handle,
const std::string &interfaceBlockName, const std::string &interfaceBlockName,
unsigned int *indexOut); unsigned int *indexOut);
// Gives the compiler-assigned register for uniforms in the default // Gives a map from uniform names to compiler-assigned registers in the default
// interface block. // interface block. Note that the map contains also registers of samplers that
// The method writes the value to the output variable "indexOut". // have been extracted from structs.
// Returns true if it found a valid default uniform, false otherwise. COMPILER_EXPORT const std::map<std::string, unsigned int> *ShGetUniformRegisterMap(
// Parameters: const ShHandle handle);
// handle: Specifies the compiler
// interfaceBlockName: Specifies the uniform
// indexOut: output variable that stores the assigned register
COMPILER_EXPORT bool ShGetUniformRegister(const ShHandle handle,
const std::string &uniformName,
unsigned int *indexOut);
#endif // GLSLANG_SHADERLANG_H_ #endif // GLSLANG_SHADERLANG_H_
...@@ -738,6 +738,16 @@ class TIntermTraverser : angle::NonCopyable ...@@ -738,6 +738,16 @@ class TIntermTraverser : angle::NonCopyable
return mPath.size() == 0 ? NULL : mPath.back(); return mPath.size() == 0 ? NULL : mPath.back();
} }
// Return the nth ancestor of the node being traversed. getAncestorNode(0) == getParentNode()
TIntermNode *getAncestorNode(unsigned int n)
{
if (mPath.size() > n)
{
return mPath[mPath.size() - n - 1u];
}
return nullptr;
}
void pushParentBlock(TIntermAggregate *node); void pushParentBlock(TIntermAggregate *node);
void incrementParentBlockPos(); void incrementParentBlockPos();
void popParentBlock(); void popParentBlock();
......
...@@ -240,6 +240,10 @@ class OutputHLSL : public TIntermTraverser ...@@ -240,6 +240,10 @@ class OutputHLSL : public TIntermTraverser
// with the other N parameters of the function. This is used to work around that arrays can't be // with the other N parameters of the function. This is used to work around that arrays can't be
// return values in HLSL. // return values in HLSL.
std::vector<ArrayHelperFunction> mArrayConstructIntoFunctions; std::vector<ArrayHelperFunction> mArrayConstructIntoFunctions;
private:
TString samplerNamePrefixFromStruct(TIntermTyped *node);
bool ancestorEvaluatesToSamplerInStruct(Visit visit);
}; };
} }
......
...@@ -363,23 +363,15 @@ bool ShGetInterfaceBlockRegister(const ShHandle handle, ...@@ -363,23 +363,15 @@ bool ShGetInterfaceBlockRegister(const ShHandle handle,
#endif // ANGLE_ENABLE_HLSL #endif // ANGLE_ENABLE_HLSL
} }
bool ShGetUniformRegister(const ShHandle handle, const std::map<std::string, unsigned int> *ShGetUniformRegisterMap(const ShHandle handle)
const std::string &uniformName,
unsigned int *indexOut)
{ {
#ifdef ANGLE_ENABLE_HLSL #ifdef ANGLE_ENABLE_HLSL
ASSERT(indexOut);
TranslatorHLSL *translator = GetTranslatorHLSLFromHandle(handle); TranslatorHLSL *translator = GetTranslatorHLSLFromHandle(handle);
ASSERT(translator); ASSERT(translator);
if (!translator->hasUniform(uniformName)) return translator->getUniformRegisterMap();
{
return false;
}
*indexOut = translator->getUniformRegister(uniformName);
return true;
#else #else
return false; static std::map<std::string, unsigned int> map;
#endif // ANGLE_ENABLE_HLSL return &map;
} #endif // ANGLE_ENABLE_HLSL
}
\ No newline at end of file
...@@ -182,26 +182,29 @@ TString StructureHLSL::define(const TStructure &structure, bool useHLSLRowMajorP ...@@ -182,26 +182,29 @@ TString StructureHLSL::define(const TStructure &structure, bool useHLSLRowMajorP
string += declareString + "\n" string += declareString + "\n"
"{\n"; "{\n";
for (unsigned int i = 0; i < fields.size(); i++) for (const TField *field : fields)
{ {
const TField &field = *fields[i]; const TType &fieldType = *field->type();
const TType &fieldType = *field.type(); if (!IsSampler(fieldType.getBasicType()))
const TStructure *fieldStruct = fieldType.getStruct();
const TString &fieldTypeString = fieldStruct ?
QualifiedStructNameString(*fieldStruct, useHLSLRowMajorPacking,
useStd140Packing) :
TypeString(fieldType);
if (padHelper)
{ {
string += padHelper->prePaddingString(fieldType); const TStructure *fieldStruct = fieldType.getStruct();
} const TString &fieldTypeString =
fieldStruct ? QualifiedStructNameString(*fieldStruct, useHLSLRowMajorPacking,
useStd140Packing)
: TypeString(fieldType);
if (padHelper)
{
string += padHelper->prePaddingString(fieldType);
}
string += " " + fieldTypeString + " " + DecorateField(field.name(), structure) + ArrayString(fieldType) + ";\n"; string += " " + fieldTypeString + " " + DecorateField(field->name(), structure) +
ArrayString(fieldType) + ";\n";
if (padHelper) if (padHelper)
{ {
string += padHelper->postPaddingString(fieldType, useHLSLRowMajorPacking); string += padHelper->postPaddingString(fieldType, useHLSLRowMajorPacking);
}
} }
} }
...@@ -265,9 +268,13 @@ TString StructureHLSL::addConstructor(const TType &type, ...@@ -265,9 +268,13 @@ TString StructureHLSL::addConstructor(const TType &type,
} }
const TFieldList &fields = structure->fields(); const TFieldList &fields = structure->fields();
for (unsigned int i = 0; i < fields.size(); i++) for (const TField *field : fields)
{ {
ctorParameters.push_back(*fields[i]->type()); const TType *fieldType = field->type();
if (!IsSampler(fieldType->getBasicType()))
{
ctorParameters.push_back(*fieldType);
}
} }
constructorFunctionName = TString(name); constructorFunctionName = TString(name);
} }
...@@ -310,7 +317,15 @@ TString StructureHLSL::addConstructor(const TType &type, ...@@ -310,7 +317,15 @@ TString StructureHLSL::addConstructor(const TType &type,
if (ctorType.getStruct()) if (ctorType.getStruct())
{ {
constructor += " " + name + " structure = {"; constructor += " " + name + " structure";
if (ctorParameters.empty())
{
constructor += ";\n";
}
else
{
constructor += " = { ";
}
} }
else else
{ {
...@@ -369,7 +384,15 @@ TString StructureHLSL::addConstructor(const TType &type, ...@@ -369,7 +384,15 @@ TString StructureHLSL::addConstructor(const TType &type,
} }
else else
{ {
size_t remainingComponents = ctorType.getObjectSize(); size_t remainingComponents = 0;
if (ctorType.getStruct())
{
remainingComponents = ctorParameters.size();
}
else
{
remainingComponents = ctorType.getObjectSize();
}
size_t parameterIndex = 0; size_t parameterIndex = 0;
while (remainingComponents > 0) while (remainingComponents > 0)
...@@ -382,10 +405,9 @@ TString StructureHLSL::addConstructor(const TType &type, ...@@ -382,10 +405,9 @@ TString StructureHLSL::addConstructor(const TType &type,
if (ctorType.getStruct()) if (ctorType.getStruct())
{ {
ASSERT(remainingComponents == parameterSize || moreParameters); ASSERT(remainingComponents == 1 || moreParameters);
ASSERT(parameterSize <= remainingComponents);
remainingComponents -= parameterSize; --remainingComponents;
} }
else if (parameter.isScalar()) else if (parameter.isScalar())
{ {
...@@ -461,9 +483,13 @@ TString StructureHLSL::addConstructor(const TType &type, ...@@ -461,9 +483,13 @@ TString StructureHLSL::addConstructor(const TType &type,
if (ctorType.getStruct()) if (ctorType.getStruct())
{ {
constructor += "};\n" if (!ctorParameters.empty())
" return structure;\n" {
"}\n"; constructor += "};\n";
}
constructor +=
" return structure;\n"
"}\n";
} }
else else
{ {
......
...@@ -72,13 +72,7 @@ unsigned int TranslatorHLSL::getInterfaceBlockRegister(const std::string &interf ...@@ -72,13 +72,7 @@ unsigned int TranslatorHLSL::getInterfaceBlockRegister(const std::string &interf
return mInterfaceBlockRegisterMap.find(interfaceBlockName)->second; return mInterfaceBlockRegisterMap.find(interfaceBlockName)->second;
} }
bool TranslatorHLSL::hasUniform(const std::string &uniformName) const const std::map<std::string, unsigned int> *TranslatorHLSL::getUniformRegisterMap() const
{ {
return (mUniformRegisterMap.count(uniformName) > 0); return &mUniformRegisterMap;
} }
\ No newline at end of file
unsigned int TranslatorHLSL::getUniformRegister(const std::string &uniformName) const
{
ASSERT(hasUniform(uniformName));
return mUniformRegisterMap.find(uniformName)->second;
}
...@@ -18,8 +18,7 @@ class TranslatorHLSL : public TCompiler ...@@ -18,8 +18,7 @@ class TranslatorHLSL : public TCompiler
bool hasInterfaceBlock(const std::string &interfaceBlockName) const; bool hasInterfaceBlock(const std::string &interfaceBlockName) const;
unsigned int getInterfaceBlockRegister(const std::string &interfaceBlockName) const; unsigned int getInterfaceBlockRegister(const std::string &interfaceBlockName) const;
bool hasUniform(const std::string &uniformName) const; const std::map<std::string, unsigned int> *getUniformRegisterMap() const;
unsigned int getUniformRegister(const std::string &uniformName) const;
protected: protected:
void translate(TIntermNode *root, int compileOptions) override; void translate(TIntermNode *root, int compileOptions) override;
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#endif #endif
#include "compiler/translator/Types.h" #include "compiler/translator/Types.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/IntermNode.h"
#include <algorithm> #include <algorithm>
#include <climits> #include <climits>
...@@ -244,6 +246,76 @@ bool TStructure::containsSamplers() const ...@@ -244,6 +246,76 @@ bool TStructure::containsSamplers() const
return false; return false;
} }
void TStructure::createSamplerSymbols(const TString &structName,
const TString &structAPIName,
const int arrayOfStructsSize,
TVector<TIntermSymbol *> *outputSymbols,
TMap<TIntermSymbol *, TString> *outputSymbolsToAPINames) const
{
for (auto &field : *mFields)
{
const TType *fieldType = field->type();
if (IsSampler(fieldType->getBasicType()))
{
if (arrayOfStructsSize > 0)
{
for (int arrayIndex = 0; arrayIndex < arrayOfStructsSize; ++arrayIndex)
{
TStringStream name;
name << structName << "_" << arrayIndex << "_" << field->name();
TIntermSymbol *symbol = new TIntermSymbol(0, name.str(), *fieldType);
outputSymbols->push_back(symbol);
if (outputSymbolsToAPINames)
{
TStringStream apiName;
apiName << structAPIName << "[" << arrayIndex << "]." << field->name();
(*outputSymbolsToAPINames)[symbol] = apiName.str();
}
}
}
else
{
TString symbolName = structName + "_" + field->name();
TIntermSymbol *symbol = new TIntermSymbol(0, symbolName, *fieldType);
outputSymbols->push_back(symbol);
if (outputSymbolsToAPINames)
{
TString apiName = structAPIName + "." + field->name();
(*outputSymbolsToAPINames)[symbol] = apiName;
}
}
}
else if (fieldType->isStructureContainingSamplers())
{
int nestedArrayOfStructsSize = fieldType->isArray() ? fieldType->getArraySize() : 0;
if (arrayOfStructsSize > 0)
{
for (int arrayIndex = 0; arrayIndex < arrayOfStructsSize; ++arrayIndex)
{
TStringStream fieldName;
fieldName << structName << "_" << arrayIndex << "_" << field->name();
TStringStream fieldAPIName;
if (outputSymbolsToAPINames)
{
fieldAPIName << structAPIName << "[" << arrayIndex << "]." << field->name();
}
fieldType->createSamplerSymbols(fieldName.str(), fieldAPIName.str(),
nestedArrayOfStructsSize, outputSymbols,
outputSymbolsToAPINames);
}
}
else
{
fieldType->createSamplerSymbols(
structName + "_" + field->name(), structAPIName + "." + field->name(),
nestedArrayOfStructsSize, outputSymbols, outputSymbolsToAPINames);
}
}
}
}
TString TFieldListCollection::buildMangledName(const TString &mangledNamePrefix) const TString TFieldListCollection::buildMangledName(const TString &mangledNamePrefix) const
{ {
TString mangledName(mangledNamePrefix); TString mangledName(mangledNamePrefix);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
struct TPublicType; struct TPublicType;
class TType; class TType;
class TSymbol; class TSymbol;
class TIntermSymbol;
class TField : angle::NonCopyable class TField : angle::NonCopyable
{ {
...@@ -120,6 +121,12 @@ class TStructure : public TFieldListCollection ...@@ -120,6 +121,12 @@ class TStructure : public TFieldListCollection
bool containsType(TBasicType t) const; bool containsType(TBasicType t) const;
bool containsSamplers() const; bool containsSamplers() const;
void createSamplerSymbols(const TString &structName,
const TString &structAPIName,
const int arrayOfStructsSize,
TVector<TIntermSymbol *> *outputSymbols,
TMap<TIntermSymbol *, TString> *outputSymbolsToAPINames) const;
bool equals(const TStructure &other) const; bool equals(const TStructure &other) const;
void setUniqueId(int uniqueId) void setUniqueId(int uniqueId)
...@@ -529,6 +536,17 @@ class TType ...@@ -529,6 +536,17 @@ class TType
return structure ? structure->containsSamplers() : false; return structure ? structure->containsSamplers() : false;
} }
void createSamplerSymbols(const TString &structName,
const TString &structAPIName,
const int arrayOfStructsSize,
TVector<TIntermSymbol *> *outputSymbols,
TMap<TIntermSymbol *, TString> *outputSymbolsToAPINames) const
{
ASSERT(structure != nullptr && structure->containsSamplers());
structure->createSamplerSymbols(structName, structAPIName, arrayOfStructsSize,
outputSymbols, outputSymbolsToAPINames);
}
// Initializes all lazily-initialized members. // Initializes all lazily-initialized members.
void realize() void realize()
{ {
......
...@@ -24,10 +24,6 @@ class UniformHLSL : angle::NonCopyable ...@@ -24,10 +24,6 @@ class UniformHLSL : angle::NonCopyable
void reserveUniformRegisters(unsigned int registerCount); void reserveUniformRegisters(unsigned int registerCount);
void reserveInterfaceBlockRegisters(unsigned int registerCount); void reserveInterfaceBlockRegisters(unsigned int registerCount);
void outputHLSLSamplerUniformGroup(TInfoSinkBase &out,
const HLSLTextureSamplerGroup textureGroup,
const TVector<const TIntermSymbol *> &group,
unsigned int *groupTextureRegisterIndex);
void uniformsHeader(TInfoSinkBase &out, void uniformsHeader(TInfoSinkBase &out,
ShShaderOutput outputType, ShShaderOutput outputType,
const ReferencedSymbols &referencedUniforms); const ReferencedSymbols &referencedUniforms);
...@@ -55,11 +51,30 @@ class UniformHLSL : angle::NonCopyable ...@@ -55,11 +51,30 @@ class UniformHLSL : angle::NonCopyable
TString interfaceBlockStructString(const TInterfaceBlock &interfaceBlock); TString interfaceBlockStructString(const TInterfaceBlock &interfaceBlock);
const Uniform *findUniformByName(const TString &name) const; const Uniform *findUniformByName(const TString &name) const;
void outputHLSL4_0_FL9_3Sampler(TInfoSinkBase &out,
const TType &type,
const TName &name,
const unsigned int registerIndex);
void outputUniform(TInfoSinkBase &out,
const TType &type,
const TName &name,
const unsigned int registerIndex);
// Returns the uniform's register index // Returns the uniform's register index
unsigned int declareUniformAndAssignRegister(const TType &type, unsigned int assignUniformRegister(const TType &type,
const TString &name, const TString &name,
unsigned int *registerCount); unsigned int *outRegisterCount);
unsigned int declareUniformAndAssignRegister(const TType &type, const TString &name); unsigned int assignSamplerInStructUniformRegister(const TType &type,
const TString &name,
unsigned int *outRegisterCount);
void outputHLSLSamplerUniformGroup(
TInfoSinkBase &out,
const HLSLTextureSamplerGroup textureGroup,
const TVector<const TIntermSymbol *> &group,
const TMap<const TIntermSymbol *, TString> &samplerInStructSymbolsToAPINames,
unsigned int *groupTextureRegisterIndex);
unsigned int mUniformRegister; unsigned int mUniformRegister;
unsigned int mInterfaceBlockRegister; unsigned int mInterfaceBlockRegister;
......
...@@ -179,14 +179,14 @@ TString TextureTypeSuffix(const TBasicType type) ...@@ -179,14 +179,14 @@ TString TextureTypeSuffix(const TBasicType type)
} }
} }
TString DecorateUniform(const TString &string, const TType &type) TString DecorateUniform(const TName &name, const TType &type)
{ {
if (type.getBasicType() == EbtSamplerExternalOES) if (type.getBasicType() == EbtSamplerExternalOES)
{ {
return "ex_" + string; return "ex_" + name.getString();
} }
return Decorate(string); return DecorateIfNeeded(name);
} }
TString DecorateField(const TString &string, const TStructure &structure) TString DecorateField(const TString &string, const TStructure &structure)
......
...@@ -64,7 +64,7 @@ TString Decorate(const TString &string); ...@@ -64,7 +64,7 @@ TString Decorate(const TString &string);
TString DecorateIfNeeded(const TName &name); TString DecorateIfNeeded(const TName &name);
// Decorates and also unmangles the function name // Decorates and also unmangles the function name
TString DecorateFunctionIfNeeded(const TName &name); TString DecorateFunctionIfNeeded(const TName &name);
TString DecorateUniform(const TString &string, const TType &type); TString DecorateUniform(const TName &name, const TType &type);
TString DecorateField(const TString &string, const TStructure &structure); TString DecorateField(const TString &string, const TStructure &structure);
TString DecoratePrivate(const TString &privateText); TString DecoratePrivate(const TString &privateText);
TString TypeString(const TType &type); TString TypeString(const TType &type);
......
...@@ -1347,16 +1347,6 @@ LinkResult ProgramD3D::link(const gl::Data &data, gl::InfoLog &infoLog) ...@@ -1347,16 +1347,6 @@ LinkResult ProgramD3D::link(const gl::Data &data, gl::InfoLog &infoLog)
{ {
reset(); reset();
// TODO(jmadill): structures containing samplers
for (const gl::LinkedUniform &linkedUniform : mData.getUniforms())
{
if (linkedUniform.isSampler() && linkedUniform.isField())
{
infoLog << "Structures containing samplers not currently supported in D3D.";
return LinkResult(false, gl::Error(GL_NO_ERROR));
}
}
const gl::Shader *vertexShader = mData.getAttachedVertexShader(); const gl::Shader *vertexShader = mData.getAttachedVertexShader();
const gl::Shader *fragmentShader = mData.getAttachedFragmentShader(); const gl::Shader *fragmentShader = mData.getAttachedFragmentShader();
...@@ -1820,7 +1810,8 @@ void ProgramD3D::defineUniformBase(const gl::Shader *shader, ...@@ -1820,7 +1810,8 @@ void ProgramD3D::defineUniformBase(const gl::Shader *shader,
const sh::Uniform &uniform, const sh::Uniform &uniform,
D3DUniformMap *uniformMap) D3DUniformMap *uniformMap)
{ {
if (uniform.isBuiltIn()) // Samplers get their registers assigned in assignAllSamplerRegisters.
if (uniform.isBuiltIn() || gl::IsSamplerType(uniform.type))
{ {
defineUniform(shader->getType(), uniform, uniform.name, nullptr, uniformMap); defineUniform(shader->getType(), uniform, uniform.name, nullptr, uniformMap);
return; return;
...@@ -1869,7 +1860,17 @@ void ProgramD3D::defineUniform(GLenum shaderType, ...@@ -1869,7 +1860,17 @@ void ProgramD3D::defineUniform(GLenum shaderType,
const sh::ShaderVariable &field = uniform.fields[fieldIndex]; const sh::ShaderVariable &field = uniform.fields[fieldIndex];
const std::string &fieldFullName = (fullName + elementString + "." + field.name); const std::string &fieldFullName = (fullName + elementString + "." + field.name);
defineUniform(shaderType, field, fieldFullName, encoder, uniformMap); // Samplers get their registers assigned in assignAllSamplerRegisters.
// Also they couldn't use the same encoder as the rest of the struct, since they are
// extracted out of the struct by the shader translator.
if (gl::IsSamplerType(field.type))
{
defineUniform(shaderType, field, fieldFullName, nullptr, uniformMap);
}
else
{
defineUniform(shaderType, field, fieldFullName, encoder, uniformMap);
}
} }
if (encoder) if (encoder)
...@@ -2066,7 +2067,7 @@ size_t ProgramD3D::getUniformBlockInfo(const sh::InterfaceBlock &interfaceBlock) ...@@ -2066,7 +2067,7 @@ size_t ProgramD3D::getUniformBlockInfo(const sh::InterfaceBlock &interfaceBlock)
void ProgramD3D::assignAllSamplerRegisters() void ProgramD3D::assignAllSamplerRegisters()
{ {
for (const D3DUniform *d3dUniform : mD3DUniforms) for (D3DUniform *d3dUniform : mD3DUniforms)
{ {
if (d3dUniform->isSampler()) if (d3dUniform->isSampler())
{ {
...@@ -2075,20 +2076,23 @@ void ProgramD3D::assignAllSamplerRegisters() ...@@ -2075,20 +2076,23 @@ void ProgramD3D::assignAllSamplerRegisters()
} }
} }
void ProgramD3D::assignSamplerRegisters(const D3DUniform *d3dUniform) void ProgramD3D::assignSamplerRegisters(D3DUniform *d3dUniform)
{ {
ASSERT(d3dUniform->isSampler()); ASSERT(d3dUniform->isSampler());
ASSERT(d3dUniform->vsRegisterIndex != GL_INVALID_INDEX || const ShaderD3D *vertexShaderD3D = GetImplAs<ShaderD3D>(mData.getAttachedVertexShader());
d3dUniform->psRegisterIndex != GL_INVALID_INDEX); const ShaderD3D *fragmentShaderD3D = GetImplAs<ShaderD3D>(mData.getAttachedFragmentShader());
ASSERT(vertexShaderD3D->hasUniform(d3dUniform) || fragmentShaderD3D->hasUniform(d3dUniform));
if (d3dUniform->vsRegisterIndex != GL_INVALID_INDEX) if (vertexShaderD3D->hasUniform(d3dUniform))
{ {
d3dUniform->vsRegisterIndex = vertexShaderD3D->getUniformRegister(d3dUniform->name);
ASSERT(d3dUniform->vsRegisterIndex != GL_INVALID_INDEX);
AssignSamplers(d3dUniform->vsRegisterIndex, d3dUniform->type, d3dUniform->arraySize, AssignSamplers(d3dUniform->vsRegisterIndex, d3dUniform->type, d3dUniform->arraySize,
mSamplersVS, &mUsedVertexSamplerRange); mSamplersVS, &mUsedVertexSamplerRange);
} }
if (fragmentShaderD3D->hasUniform(d3dUniform))
if (d3dUniform->psRegisterIndex != GL_INVALID_INDEX)
{ {
d3dUniform->psRegisterIndex = fragmentShaderD3D->getUniformRegister(d3dUniform->name);
ASSERT(d3dUniform->psRegisterIndex != GL_INVALID_INDEX);
AssignSamplers(d3dUniform->psRegisterIndex, d3dUniform->type, d3dUniform->arraySize, AssignSamplers(d3dUniform->psRegisterIndex, d3dUniform->type, d3dUniform->arraySize,
mSamplersPS, &mUsedPixelSamplerRange); mSamplersPS, &mUsedPixelSamplerRange);
} }
......
...@@ -322,7 +322,7 @@ class ProgramD3D : public ProgramImpl ...@@ -322,7 +322,7 @@ class ProgramD3D : public ProgramImpl
sh::HLSLBlockEncoder *encoder, sh::HLSLBlockEncoder *encoder,
D3DUniformMap *uniformMap); D3DUniformMap *uniformMap);
void assignAllSamplerRegisters(); void assignAllSamplerRegisters();
void assignSamplerRegisters(const D3DUniform *d3dUniform); void assignSamplerRegisters(D3DUniform *d3dUniform);
static void AssignSamplers(unsigned int startSamplerIndex, static void AssignSamplers(unsigned int startSamplerIndex,
GLenum samplerType, GLenum samplerType,
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "libANGLE/Shader.h" #include "libANGLE/Shader.h"
#include "libANGLE/features.h" #include "libANGLE/features.h"
#include "libANGLE/renderer/d3d/RendererD3D.h" #include "libANGLE/renderer/d3d/RendererD3D.h"
#include "libANGLE/renderer/d3d/ProgramD3D.h"
// Definitions local to the translation unit // Definitions local to the translation unit
namespace namespace
...@@ -139,6 +140,18 @@ int ShaderD3D::prepareSourceAndReturnOptions(std::stringstream *shaderSourceStre ...@@ -139,6 +140,18 @@ int ShaderD3D::prepareSourceAndReturnOptions(std::stringstream *shaderSourceStre
return additionalOptions; return additionalOptions;
} }
bool ShaderD3D::hasUniform(const D3DUniform *d3dUniform) const
{
return mUniformRegisterMap.find(d3dUniform->name) != mUniformRegisterMap.end();
}
const std::map<std::string, unsigned int> &GetUniformRegisterMap(
const std::map<std::string, unsigned int> *uniformRegisterMap)
{
ASSERT(uniformRegisterMap);
return *uniformRegisterMap;
}
bool ShaderD3D::postTranslateCompile(gl::Compiler *compiler, std::string *infoLog) bool ShaderD3D::postTranslateCompile(gl::Compiler *compiler, std::string *infoLog)
{ {
// TODO(jmadill): We shouldn't need to cache this. // TODO(jmadill): We shouldn't need to cache this.
...@@ -164,19 +177,7 @@ bool ShaderD3D::postTranslateCompile(gl::Compiler *compiler, std::string *infoLo ...@@ -164,19 +177,7 @@ bool ShaderD3D::postTranslateCompile(gl::Compiler *compiler, std::string *infoLo
ShHandle compilerHandle = compiler->getCompilerHandle(mData.getShaderType()); ShHandle compilerHandle = compiler->getCompilerHandle(mData.getShaderType());
for (const sh::Uniform &uniform : mData.getUniforms()) mUniformRegisterMap = GetUniformRegisterMap(ShGetUniformRegisterMap(compilerHandle));
{
if (uniform.staticUse && !uniform.isBuiltIn())
{
unsigned int index = static_cast<unsigned int>(-1);
bool getUniformRegisterResult =
ShGetUniformRegister(compilerHandle, uniform.name, &index);
UNUSED_ASSERTION_VARIABLE(getUniformRegisterResult);
ASSERT(getUniformRegisterResult);
mUniformRegisterMap[uniform.name] = index;
}
}
for (const sh::InterfaceBlock &interfaceBlock : mData.getInterfaceBlocks()) for (const sh::InterfaceBlock &interfaceBlock : mData.getInterfaceBlocks())
{ {
......
...@@ -18,6 +18,7 @@ namespace rx ...@@ -18,6 +18,7 @@ namespace rx
class DynamicHLSL; class DynamicHLSL;
class RendererD3D; class RendererD3D;
struct D3DCompilerWorkarounds; struct D3DCompilerWorkarounds;
struct D3DUniform;
class ShaderD3D : public ShaderImpl class ShaderD3D : public ShaderImpl
{ {
...@@ -33,7 +34,13 @@ class ShaderD3D : public ShaderImpl ...@@ -33,7 +34,13 @@ class ShaderD3D : public ShaderImpl
// D3D-specific methods // D3D-specific methods
void uncompile(); void uncompile();
bool hasUniform(const D3DUniform *d3dUniform) const;
// Query regular uniforms with their name. Query sampler fields of structs with field selection
// using dot (.) operator.
unsigned int getUniformRegister(const std::string &uniformName) const; unsigned int getUniformRegister(const std::string &uniformName) const;
unsigned int getInterfaceBlockRegister(const std::string &blockName) const; unsigned int getInterfaceBlockRegister(const std::string &blockName) const;
void appendDebugInfo(const std::string &info) const { mDebugInfo += info; } void appendDebugInfo(const std::string &info) const { mDebugInfo += info; }
......
...@@ -101,12 +101,6 @@ ...@@ -101,12 +101,6 @@
1017 WIN : dEQP-GLES2.functional.shaders.loops.while_constant_iterations.nested_tricky_dataflow_* = FAIL 1017 WIN : dEQP-GLES2.functional.shaders.loops.while_constant_iterations.nested_tricky_dataflow_* = FAIL
1017 WIN : dEQP-GLES2.functional.shaders.loops.do_while_constant_iterations.nested_tricky_dataflow_* = FAIL 1017 WIN : dEQP-GLES2.functional.shaders.loops.do_while_constant_iterations.nested_tricky_dataflow_* = FAIL
989 WIN : dEQP-GLES2.functional.shaders.preprocessor.pragmas.invalid_pragma_invalid_* = FAIL 989 WIN : dEQP-GLES2.functional.shaders.preprocessor.pragmas.invalid_pragma_invalid_* = FAIL
504 WIN : dEQP-GLES2.functional.shaders.struct.uniform.sampler_vertex = FAIL
504 WIN : dEQP-GLES2.functional.shaders.struct.uniform.sampler_fragment = FAIL
504 WIN : dEQP-GLES2.functional.shaders.struct.uniform.sampler_nested_vertex = FAIL
504 WIN : dEQP-GLES2.functional.shaders.struct.uniform.sampler_nested_fragment = FAIL
504 WIN : dEQP-GLES2.functional.shaders.struct.uniform.sampler_array_vertex = FAIL
504 WIN : dEQP-GLES2.functional.shaders.struct.uniform.sampler_array_fragment = FAIL
1018 WIN : dEQP-GLES2.functional.shaders.texture_functions.vertex.texturecubelod = FAIL 1018 WIN : dEQP-GLES2.functional.shaders.texture_functions.vertex.texturecubelod = FAIL
1020 WIN : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_nearest = FAIL 1020 WIN : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_nearest = FAIL
1020 WIN : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_linear = FAIL 1020 WIN : dEQP-GLES2.functional.texture.mipmap.cube.basic.linear_linear = FAIL
...@@ -144,51 +138,6 @@ ...@@ -144,51 +138,6 @@
1025 WIN : dEQP-GLES2.functional.fragment_ops.blend.equation_src_func_dst_func.reverse_subtract_constant_alpha_one_minus_constant_color = FAIL 1025 WIN : dEQP-GLES2.functional.fragment_ops.blend.equation_src_func_dst_func.reverse_subtract_constant_alpha_one_minus_constant_color = FAIL
1025 WIN : dEQP-GLES2.functional.fragment_ops.blend.equation_src_func_dst_func.reverse_subtract_one_minus_constant_alpha_constant_color = FAIL 1025 WIN : dEQP-GLES2.functional.fragment_ops.blend.equation_src_func_dst_func.reverse_subtract_one_minus_constant_alpha_constant_color = FAIL
1025 WIN : dEQP-GLES2.functional.fragment_ops.blend.equation_src_func_dst_func.reverse_subtract_one_minus_constant_alpha_one_minus_constant_color = FAIL 1025 WIN : dEQP-GLES2.functional.fragment_ops.blend.equation_src_func_dst_func.reverse_subtract_one_minus_constant_alpha_one_minus_constant_color = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.info_query.basic_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.info_query.struct_in_array.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.info_query.array_in_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.info_query.nested_structs_arrays.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.info_query.unused_uniforms.sampler2D_samplerCube_* = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.3 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.13 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.21 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.23 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.24 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.25 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.27 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.29 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.37 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.41 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.51 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.54 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.61 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.72 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.79 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.82 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.87 = FAIL
1031 WIN : dEQP-GLES2.functional.uniform_api.random.93 = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.basic_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.struct_in_array.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.array_in_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.initial.get_uniform.nested_structs_arrays.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.struct_in_array.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.basic_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.array_in_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.get_uniform.nested_structs_arrays.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.basic_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.struct_in_array.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.array_in_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_pointer.render.nested_structs_arrays.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.basic_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.struct_in_array.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.array_in_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.get_uniform.nested_structs_arrays.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.basic_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.struct_in_array.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.by_value.render.array_in_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.basic_array_assign_full.array_in_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.basic_array_assign_partial.array_in_struct.sampler2D_samplerCube_* = FAIL
504 WIN : dEQP-GLES2.functional.uniform_api.value.assigned.unused_uniforms.sampler2D_samplerCube_* = FAIL
// Failures on the dEQP Windows bots that do not reproduce locally // Failures on the dEQP Windows bots that do not reproduce locally
1108 WIN : dEQP-GLES2.functional.shaders.struct.local.dynamic_loop_struct_array_fragment = FAIL 1108 WIN : dEQP-GLES2.functional.shaders.struct.local.dynamic_loop_struct_array_fragment = FAIL
......
...@@ -139,6 +139,8 @@ class Texture2DTest : public TexCoordDrawTest ...@@ -139,6 +139,8 @@ class Texture2DTest : public TexCoordDrawTest
); );
} }
virtual const char *getTextureUniformName() { return "tex"; }
void SetUp() override void SetUp() override
{ {
TexCoordDrawTest::SetUp(); TexCoordDrawTest::SetUp();
...@@ -146,7 +148,7 @@ class Texture2DTest : public TexCoordDrawTest ...@@ -146,7 +148,7 @@ class Texture2DTest : public TexCoordDrawTest
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
mTexture2DUniformLocation = glGetUniformLocation(mProgram, "tex"); mTexture2DUniformLocation = glGetUniformLocation(mProgram, getTextureUniformName());
ASSERT_NE(-1, mTexture2DUniformLocation); ASSERT_NE(-1, mTexture2DUniformLocation);
} }
...@@ -921,6 +923,152 @@ class SamplerTypeMixTestES3 : public TexCoordDrawTest ...@@ -921,6 +923,152 @@ class SamplerTypeMixTestES3 : public TexCoordDrawTest
GLint mDepthRefUniformLocation; GLint mDepthRefUniformLocation;
}; };
class SamplerInStructTest : public Texture2DTest
{
protected:
SamplerInStructTest() : Texture2DTest() {}
const char *getTextureUniformName() override { return "us.tex"; }
std::string getFragmentShaderSource() override
{
return std::string(
"precision highp float;\n"
"struct S\n"
"{\n"
" vec4 a;\n"
" highp sampler2D tex;\n"
"};\n"
"uniform S us;\n"
"varying vec2 texcoord;\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2D(us.tex, texcoord + us.a.x);\n"
"}\n");
}
void runSamplerInStructTest()
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTexture2D);
GLubyte texDataGreen[1u * 1u * 4u];
FillWithRGBA<GLubyte>(1u * 1u, 0u, 255u, 0u, 255u, texDataGreen);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texDataGreen);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_EQ(0, 0, 0, 255, 0, 255);
}
};
class SamplerInStructAsFunctionParameterTest : public SamplerInStructTest
{
protected:
SamplerInStructAsFunctionParameterTest() : SamplerInStructTest() {}
std::string getFragmentShaderSource() override
{
return std::string(
"precision highp float;\n"
"struct S\n"
"{\n"
" vec4 a;\n"
" highp sampler2D tex;\n"
"};\n"
"uniform S us;\n"
"varying vec2 texcoord;\n"
"vec4 sampleFrom(S s) {\n"
" return texture2D(s.tex, texcoord + s.a.x);\n"
"}\n"
"void main()\n"
"{\n"
" gl_FragColor = sampleFrom(us);\n"
"}\n");
}
};
class SamplerInStructArrayAsFunctionParameterTest : public SamplerInStructTest
{
protected:
SamplerInStructArrayAsFunctionParameterTest() : SamplerInStructTest() {}
const char *getTextureUniformName() override { return "us[0].tex"; }
std::string getFragmentShaderSource() override
{
return std::string(
"precision highp float;\n"
"struct S\n"
"{\n"
" vec4 a;\n"
" highp sampler2D tex;\n"
"};\n"
"uniform S us[1];\n"
"varying vec2 texcoord;\n"
"vec4 sampleFrom(S s) {\n"
" return texture2D(s.tex, texcoord + s.a.x);\n"
"}\n"
"void main()\n"
"{\n"
" gl_FragColor = sampleFrom(us[0]);\n"
"}\n");
}
};
class SamplerInNestedStructAsFunctionParameterTest : public SamplerInStructTest
{
protected:
SamplerInNestedStructAsFunctionParameterTest() : SamplerInStructTest() {}
const char *getTextureUniformName() override { return "us[0].sub.tex"; }
std::string getFragmentShaderSource() override
{
return std::string(
"precision highp float;\n"
"struct SUB\n"
"{\n"
" vec4 a;\n"
" highp sampler2D tex;\n"
"};\n"
"struct S\n"
"{\n"
" SUB sub;\n"
"};\n"
"uniform S us[1];\n"
"varying vec2 texcoord;\n"
"vec4 sampleFrom(SUB s) {\n"
" return texture2D(s.tex, texcoord + s.a.x);\n"
"}\n"
"void main()\n"
"{\n"
" gl_FragColor = sampleFrom(us[0].sub);\n"
"}\n");
}
};
class SamplerInStructAndOtherVariableTest : public SamplerInStructTest
{
protected:
SamplerInStructAndOtherVariableTest() : SamplerInStructTest() {}
std::string getFragmentShaderSource() override
{
return std::string(
"precision highp float;\n"
"struct S\n"
"{\n"
" vec4 a;\n"
" highp sampler2D tex;\n"
"};\n"
"uniform S us;\n"
"uniform float us_tex;\n"
"varying vec2 texcoord;\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2D(us.tex, texcoord + us.a.x + us_tex);\n"
"}\n");
}
};
TEST_P(Texture2DTest, NegativeAPISubImage) TEST_P(Texture2DTest, NegativeAPISubImage)
{ {
glBindTexture(GL_TEXTURE_2D, mTexture2D); glBindTexture(GL_TEXTURE_2D, mTexture2D);
...@@ -1864,6 +2012,39 @@ TEST_P(Texture2DTestES3, TextureCOMPRESSEDSRGB8ETC2ImplicitAlpha1) ...@@ -1864,6 +2012,39 @@ TEST_P(Texture2DTestES3, TextureCOMPRESSEDSRGB8ETC2ImplicitAlpha1)
EXPECT_PIXEL_ALPHA_EQ(0, 0, 255); EXPECT_PIXEL_ALPHA_EQ(0, 0, 255);
} }
// Use a sampler in a uniform struct.
TEST_P(SamplerInStructTest, SamplerInStruct)
{
runSamplerInStructTest();
}
// Use a sampler in a uniform struct that's passed as a function parameter.
TEST_P(SamplerInStructAsFunctionParameterTest, SamplerInStructAsFunctionParameter)
{
runSamplerInStructTest();
}
// Use a sampler in a uniform struct array with a struct from the array passed as a function
// parameter.
TEST_P(SamplerInStructArrayAsFunctionParameterTest, SamplerInStructArrayAsFunctionParameter)
{
runSamplerInStructTest();
}
// Use a sampler in a struct inside a uniform struct with the nested struct passed as a function
// parameter.
TEST_P(SamplerInNestedStructAsFunctionParameterTest, SamplerInNestedStructAsFunctionParameter)
{
runSamplerInStructTest();
}
// Make sure that there isn't a name conflict between sampler extracted from a struct and a
// similarly named uniform.
TEST_P(SamplerInStructAndOtherVariableTest, SamplerInStructAndOtherVariable)
{
runSamplerInStructTest();
}
class TextureLimitsTest : public ANGLETest class TextureLimitsTest : public ANGLETest
{ {
protected: protected:
...@@ -2310,6 +2491,36 @@ ANGLE_INSTANTIATE_TEST(ShadowSamplerPlusSampler3DTestES3, ...@@ -2310,6 +2491,36 @@ ANGLE_INSTANTIATE_TEST(ShadowSamplerPlusSampler3DTestES3,
ANGLE_INSTANTIATE_TEST(SamplerTypeMixTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); ANGLE_INSTANTIATE_TEST(SamplerTypeMixTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(Texture2DArrayTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES()); ANGLE_INSTANTIATE_TEST(Texture2DArrayTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(TextureSizeTextureArrayTest, ES3_D3D11(), ES3_OPENGL()); ANGLE_INSTANTIATE_TEST(TextureSizeTextureArrayTest, ES3_D3D11(), ES3_OPENGL());
ANGLE_INSTANTIATE_TEST(SamplerInStructTest,
ES2_D3D11(),
ES2_D3D11_FL9_3(),
ES2_D3D9(),
ES2_OPENGL(),
ES2_OPENGLES());
ANGLE_INSTANTIATE_TEST(SamplerInStructAsFunctionParameterTest,
ES2_D3D11(),
ES2_D3D11_FL9_3(),
ES2_D3D9(),
ES2_OPENGL(),
ES2_OPENGLES());
ANGLE_INSTANTIATE_TEST(SamplerInStructArrayAsFunctionParameterTest,
ES2_D3D11(),
ES2_D3D11_FL9_3(),
ES2_D3D9(),
ES2_OPENGL(),
ES2_OPENGLES());
ANGLE_INSTANTIATE_TEST(SamplerInNestedStructAsFunctionParameterTest,
ES2_D3D11(),
ES2_D3D11_FL9_3(),
ES2_D3D9(),
ES2_OPENGL(),
ES2_OPENGLES());
ANGLE_INSTANTIATE_TEST(SamplerInStructAndOtherVariableTest,
ES2_D3D11(),
ES2_D3D11_FL9_3(),
ES2_D3D9(),
ES2_OPENGL(),
ES2_OPENGLES());
ANGLE_INSTANTIATE_TEST(TextureLimitsTest, ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES()); ANGLE_INSTANTIATE_TEST(TextureLimitsTest, ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES());
} // namespace } // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment