Commit a735ee2f by Qin Jiajia Committed by Commit Bot

ES31: Support shader storage block in D3D11 compiler - Part1

This patch is the first step to implement a basic skeleton to translate shader storage block to HLSL RWByteAddressBuffer. In GLSL each shader storage block is just one structured block and in API side it corresponds to a buffer range where stores the whole structure. RWStructuredBuffer is an array-like object and can have many structured elements. The structured element doesn't support unsized array and also have a small limitation on the element size. So we choose RWByteAddressBuffer as the counterpart of shader storage block in HLSL. Due to RWByteAddressBuffer does not support using an index to reference a specific location, we must use Load and Store to process the read/write operation of a buffer variable. Moreover, in the compiler tree, since we can't use variable name to get the resource value in RWByteAddressBuffer, we have to calculate the offset of buffer variable in a shader storage block, then call the corresponding wrapper function to get the right value. In this patch, we only process below situations: assign_to_ssbo := ssbo_access_chain = expr_no_ssbo; assign_from_ssbo := lvalue_no_ssbo = ssbo_access_chain; The translation is like below: // GLSL #version 310 es layout(local_size_x=8) in; layout(std140, binding = 0) buffer blockA { float f[8]; } instanceA; layout(std140, binding = 1) buffer blockB { float f[8]; }; void main() { float data = instanceA.f[gl_LocalInvocationIndex]; f[gl_LocalInvocationIndex] = data; } // HLSL RWByteAddressBuffer _instanceA: register(u0); RWByteAddressBuffer _blockB: register(u1); float float_Load(RWByteAddressBuffer buffer, uint loc) { float result = asfloat(buffer.Load(loc)); return result; } void float_Store(RWByteAddressBuffer buffer, uint loc, float value) { buffer.Store(loc, asuint(value)); } void gl_main() { float _data = float_Load(_instanceA, 0 + 16 * gl_LocalInvocationIndex); float_Store(_blockB, 0 + 16 * gl_LocalInvocationIndex, _data); } We will do below things in the following patches: 1. Modify the intermediate tree to flatten all ssbo usages to: assign_to_ssbo := ssbo_access_chain = expr_no_ssbo; assign_from_ssbo := lvalue_no_ssbo = ssbo_access_chain; e.g. intanceA.a +=1; ->tmp = intanceA.a; intanceA.a = tmp + 1; while(++instanceA.a < 16) { } -> int PreIncrement(out int a) { a += 1; return a; } tmp = instanceA.a; while(PreIncrement(tmp) < 16) { instanceA.a = tmp } 2. Add offset calculation for structure and array of arrays. TODOs have been marked in the corresponding places in this patch. 3. Improve helper functions so that they can process all possible types. TODOs have been marked in the corresponding places in this patch. 4. Process the swizzle situation. TODOs have been marked in the corresponding places in this patch. A possible method is to extend current helper functions like below: *_Load(RWByteAddressBuffer buffer, uint loc, bool isSwizzle, uint4 swizzleOffset) Bug: angleproject:1951 Test: angle_end2end_tests Change-Id: I68ae68d5bb77d0d5627c8272627a7f689b8dc38b Reviewed-on: https://chromium-review.googlesource.com/848215Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Jiajia Qin <jiajia.qin@intel.com>
parent 1cee0421
......@@ -229,6 +229,10 @@ angle_translator_hlsl_sources = [
"src/compiler/translator/OutputHLSL.h",
"src/compiler/translator/ResourcesHLSL.cpp",
"src/compiler/translator/ResourcesHLSL.h",
"src/compiler/translator/ShaderStorageBlockFunctionHLSL.cpp",
"src/compiler/translator/ShaderStorageBlockFunctionHLSL.h",
"src/compiler/translator/ShaderStorageBlockOutputHLSL.cpp",
"src/compiler/translator/ShaderStorageBlockOutputHLSL.h",
"src/compiler/translator/StructureHLSL.cpp",
"src/compiler/translator/StructureHLSL.h",
"src/compiler/translator/TextureFunctionHLSL.cpp",
......
......@@ -59,22 +59,25 @@ bool IsDeclarationWrittenOut(TIntermDeclaration *node)
variable->getQualifier() == EvqConst || variable->getQualifier() == EvqShared);
}
bool IsInStd140InterfaceBlock(TIntermTyped *node)
bool IsInStd140UniformBlock(TIntermTyped *node)
{
TIntermBinary *binaryNode = node->getAsBinaryNode();
if (binaryNode)
{
return IsInStd140InterfaceBlock(binaryNode->getLeft());
return IsInStd140UniformBlock(binaryNode->getLeft());
}
const TType &type = node->getType();
// determine if we are in the standard layout
const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
if (interfaceBlock)
if (type.getQualifier() == EvqUniform)
{
return (interfaceBlock->blockStorage() == EbsStd140);
// determine if we are in the standard layout
const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
if (interfaceBlock)
{
return (interfaceBlock->blockStorage() == EbsStd140);
}
}
return false;
......@@ -249,10 +252,13 @@ OutputHLSL::OutputHLSL(sh::GLenum shaderType,
// Reserve registers for the default uniform block and driver constants
mResourcesHLSL->reserveUniformBlockRegisters(2);
mSSBOOutputHLSL = new ShaderStorageBlockOutputHLSL(this, symbolTable, mResourcesHLSL);
}
OutputHLSL::~OutputHLSL()
{
SafeDelete(mSSBOOutputHLSL);
SafeDelete(mStructureHLSL);
SafeDelete(mResourcesHLSL);
SafeDelete(mTextureFunctionHLSL);
......@@ -384,9 +390,20 @@ TString OutputHLSL::generateStructMapping(const std::vector<MappedStruct> &std14
{
const TInterfaceBlock *interfaceBlock =
mappedStruct.blockDeclarator->getType().getInterfaceBlock();
if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0)
TQualifier qualifier = mappedStruct.blockDeclarator->getType().getQualifier();
switch (qualifier)
{
continue;
case EvqUniform:
if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0)
{
continue;
}
break;
case EvqBuffer:
continue;
default:
UNREACHABLE();
return mappedStructs;
}
unsigned int instanceCount = 1u;
......@@ -409,7 +426,7 @@ TString OutputHLSL::generateStructMapping(const std::vector<MappedStruct> &std14
unsigned int instanceStringArrayIndex = GL_INVALID_INDEX;
if (isInstanceArray)
instanceStringArrayIndex = instanceArrayIndex;
TString instanceString = mResourcesHLSL->UniformBlockInstanceString(
TString instanceString = mResourcesHLSL->InterfaceBlockInstanceString(
instanceName, instanceStringArrayIndex);
originalName += instanceString;
mappedName += instanceString;
......@@ -473,6 +490,7 @@ void OutputHLSL::header(TInfoSinkBase &out,
mResourcesHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms, mSymbolTable);
out << mResourcesHLSL->uniformBlocksHeader(mReferencedUniformBlocks);
mSSBOOutputHLSL->writeShaderStorageBlocksHeader(out);
if (!mEqualityFunctions.empty())
{
......@@ -905,7 +923,7 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node)
TInfoSinkBase &out = getInfoSink();
// Handle accessing std140 structs by value
if (IsInStd140InterfaceBlock(node) && node->getBasicType() == EbtStruct)
if (IsInStd140UniformBlock(node) && node->getBasicType() == EbtStruct)
{
out << "map";
}
......@@ -949,6 +967,10 @@ void OutputHLSL::visitSymbol(TIntermSymbol *node)
out << DecorateVariableIfNeeded(variable);
}
else if (qualifier == EvqBuffer)
{
UNREACHABLE();
}
else if (qualifier == EvqAttribute || qualifier == EvqVertexIn)
{
mReferencedAttributes[uniqueId.get()] = &variable;
......@@ -1193,6 +1215,29 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
out << ")";
return false;
}
else if (IsInShaderStorageBlock(node->getLeft()))
{
mSSBOOutputHLSL->outputStoreFunctionCallPrefix(node->getLeft());
out << ", ";
if (IsInShaderStorageBlock(node->getRight()))
{
mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight());
}
else
{
node->getRight()->traverse(this);
}
out << ")";
return false;
}
else if (IsInShaderStorageBlock(node->getRight()))
{
node->getLeft()->traverse(this);
out << " = ";
mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight());
return false;
}
outputAssign(visit, node->getType(), out);
break;
......@@ -1224,6 +1269,11 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
else if (visit == InVisit)
{
out << " = ";
if (IsInShaderStorageBlock(node->getRight()))
{
mSSBOOutputHLSL->outputLoadFunctionCall(node->getRight());
return false;
}
}
break;
case EOpAddAssign:
......@@ -1303,13 +1353,15 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
{
TIntermSymbol *instanceArraySymbol = node->getLeft()->getAsSymbolNode();
const TInterfaceBlock *interfaceBlock = leftType.getInterfaceBlock();
ASSERT(leftType.getQualifier() == EvqUniform);
if (mReferencedUniformBlocks.count(interfaceBlock->uniqueId().get()) == 0)
{
mReferencedUniformBlocks[interfaceBlock->uniqueId().get()] =
new TReferencedBlock(interfaceBlock, &instanceArraySymbol->variable());
}
const int arrayIndex = node->getRight()->getAsConstantUnion()->getIConst(0);
out << mResourcesHLSL->UniformBlockInstanceString(
out << mResourcesHLSL->InterfaceBlockInstanceString(
instanceArraySymbol->getName(), arrayIndex);
return false;
}
......@@ -1370,9 +1422,10 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
break;
case EOpIndexDirectInterfaceBlock:
{
bool structInStd140Block =
node->getBasicType() == EbtStruct && IsInStd140InterfaceBlock(node->getLeft());
if (visit == PreVisit && structInStd140Block)
ASSERT(!IsInShaderStorageBlock(node->getLeft()));
bool structInStd140UniformBlock =
node->getBasicType() == EbtStruct && IsInStd140UniformBlock(node->getLeft());
if (visit == PreVisit && structInStd140UniformBlock)
{
out << "map";
}
......@@ -1382,7 +1435,7 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
node->getLeft()->getType().getInterfaceBlock();
const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
const TField *field = interfaceBlock->fields()[index->getIConst(0)];
if (structInStd140Block)
if (structInStd140UniformBlock)
{
out << "_";
}
......
......@@ -16,6 +16,7 @@
#include "compiler/translator/Compiler.h"
#include "compiler/translator/FlagStd140Structs.h"
#include "compiler/translator/ImmutableString.h"
#include "compiler/translator/ShaderStorageBlockOutputHLSL.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
class BuiltInFunctionEmulator;
......@@ -30,16 +31,6 @@ class TSymbolTable;
class TVariable;
class UnfoldShortCircuit;
struct TReferencedBlock : angle::NonCopyable
{
POOL_ALLOCATOR_NEW_DELETE();
TReferencedBlock(const TInterfaceBlock *block, const TVariable *instanceVariable);
const TInterfaceBlock *block;
const TVariable *instanceVariable; // May be nullptr if the block is not instanced.
};
// Maps from uniqueId to a variable.
using ReferencedInterfaceBlocks = std::map<int, const TReferencedBlock *>;
using ReferencedVariables = std::map<int, const TVariable *>;
class OutputHLSL : public TIntermTraverser
......@@ -73,6 +64,8 @@ class OutputHLSL : public TIntermTraverser
}
protected:
friend class ShaderStorageBlockOutputHLSL;
void writeReferencedAttributes(TInfoSinkBase &out) const;
void writeReferencedVaryings(TInfoSinkBase &out) const;
void header(TInfoSinkBase &out,
......@@ -263,6 +256,7 @@ class OutputHLSL : public TIntermTraverser
TString generateStructMapping(const std::vector<MappedStruct> &std140Structs) const;
ImmutableString samplerNamePrefixFromStruct(TIntermTyped *node);
bool ancestorEvaluatesToSamplerInStruct();
ShaderStorageBlockOutputHLSL *mSSBOOutputHLSL;
};
}
......
......@@ -102,7 +102,7 @@ ResourcesHLSL::ResourcesHLSL(StructureHLSL *structureHLSL,
: mUniformRegister(firstUniformRegister),
mUniformBlockRegister(0),
mTextureRegister(0),
mRWTextureRegister(0),
mUAVRegister(0),
mSamplerCount(0),
mStructureHLSL(structureHLSL),
mOutputType(outputType),
......@@ -148,7 +148,7 @@ unsigned int ResourcesHLSL::assignUniformRegister(const TType &type,
}
else if (IsImage(type.getBasicType()))
{
registerIndex = mRWTextureRegister;
registerIndex = mUAVRegister;
}
else
{
......@@ -166,7 +166,7 @@ unsigned int ResourcesHLSL::assignUniformRegister(const TType &type,
}
else if (IsImage(type.getBasicType()))
{
mRWTextureRegister += registerCount;
mUAVRegister += registerCount;
}
else
{
......@@ -553,6 +553,40 @@ TString ResourcesHLSL::uniformBlocksHeader(
return (interfaceBlocks.empty() ? "" : ("// Uniform Blocks\n\n" + interfaceBlocks));
}
TString ResourcesHLSL::shaderStorageBlocksHeader(
const ReferencedInterfaceBlocks &referencedInterfaceBlocks)
{
TString interfaceBlocks;
for (const auto &interfaceBlockReference : referencedInterfaceBlocks)
{
const TInterfaceBlock &interfaceBlock = *interfaceBlockReference.second->block;
const TVariable *instanceVariable = interfaceBlockReference.second->instanceVariable;
unsigned int activeRegister = mUAVRegister;
mShaderStorageBlockRegisterMap[interfaceBlock.name().data()] = activeRegister;
if (instanceVariable != nullptr && instanceVariable->getType().isArray())
{
unsigned int instanceArraySize = instanceVariable->getType().getOutermostArraySize();
for (unsigned int arrayIndex = 0; arrayIndex < instanceArraySize; arrayIndex++)
{
interfaceBlocks += shaderStorageBlockString(
interfaceBlock, instanceVariable, activeRegister + arrayIndex, arrayIndex);
}
mUAVRegister += instanceArraySize;
}
else
{
interfaceBlocks += shaderStorageBlockString(interfaceBlock, instanceVariable,
activeRegister, GL_INVALID_INDEX);
mUAVRegister += 1u;
}
}
return (interfaceBlocks.empty() ? "" : ("// Shader Storage Blocks\n\n" + interfaceBlocks));
}
TString ResourcesHLSL::uniformBlockString(const TInterfaceBlock &interfaceBlock,
const TVariable *instanceVariable,
unsigned int registerIndex,
......@@ -569,7 +603,7 @@ TString ResourcesHLSL::uniformBlockString(const TInterfaceBlock &interfaceBlock,
if (instanceVariable != nullptr)
{
hlsl += " " + InterfaceBlockStructName(interfaceBlock) + " " +
UniformBlockInstanceString(instanceVariable->name(), arrayIndex) + ";\n";
InterfaceBlockInstanceString(instanceVariable->name(), arrayIndex) + ";\n";
}
else
{
......@@ -582,8 +616,28 @@ TString ResourcesHLSL::uniformBlockString(const TInterfaceBlock &interfaceBlock,
return hlsl;
}
TString ResourcesHLSL::UniformBlockInstanceString(const ImmutableString &instanceName,
unsigned int arrayIndex)
TString ResourcesHLSL::shaderStorageBlockString(const TInterfaceBlock &interfaceBlock,
const TVariable *instanceVariable,
unsigned int registerIndex,
unsigned int arrayIndex)
{
TString hlsl;
if (instanceVariable != nullptr)
{
hlsl += "RWByteAddressBuffer " +
InterfaceBlockInstanceString(instanceVariable->name(), arrayIndex) +
": register(u" + str(registerIndex) + ");\n";
}
else
{
hlsl += "RWByteAddressBuffer " + Decorate(interfaceBlock.name()) + ": register(u" +
str(registerIndex) + ");\n";
}
return hlsl;
}
TString ResourcesHLSL::InterfaceBlockInstanceString(const ImmutableString &instanceName,
unsigned int arrayIndex)
{
if (arrayIndex != GL_INVALID_INDEX)
{
......
......@@ -38,10 +38,11 @@ class ResourcesHLSL : angle::NonCopyable
void samplerMetadataUniforms(TInfoSinkBase &out, const char *reg);
TString uniformBlocksHeader(const ReferencedInterfaceBlocks &referencedInterfaceBlocks);
TString shaderStorageBlocksHeader(const ReferencedInterfaceBlocks &referencedInterfaceBlocks);
// Used for direct index references
static TString UniformBlockInstanceString(const ImmutableString &instanceName,
unsigned int arrayIndex);
static TString InterfaceBlockInstanceString(const ImmutableString &instanceName,
unsigned int arrayIndex);
const std::map<std::string, unsigned int> &getUniformBlockRegisterMap() const
{
......@@ -57,6 +58,11 @@ class ResourcesHLSL : angle::NonCopyable
const TVariable *instanceVariable,
unsigned int registerIndex,
unsigned int arrayIndex);
TString shaderStorageBlockString(const TInterfaceBlock &interfaceBlock,
const TVariable *instanceVariable,
unsigned int registerIndex,
unsigned int arrayIndex);
TString uniformBlockMembersString(const TInterfaceBlock &interfaceBlock,
TLayoutBlockStorage blockStorage);
TString uniformBlockStructString(const TInterfaceBlock &interfaceBlock);
......@@ -104,13 +110,14 @@ class ResourcesHLSL : angle::NonCopyable
unsigned int mUniformRegister;
unsigned int mUniformBlockRegister;
unsigned int mTextureRegister;
unsigned int mRWTextureRegister;
unsigned int mUAVRegister;
unsigned int mSamplerCount;
StructureHLSL *mStructureHLSL;
ShShaderOutput mOutputType;
const std::vector<Uniform> &mUniforms;
std::map<std::string, unsigned int> mUniformBlockRegisterMap;
std::map<std::string, unsigned int> mShaderStorageBlockRegisterMap;
std::map<std::string, unsigned int> mUniformRegisterMap;
};
}
......
//
// Copyright 2018 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.
//
// ShaderStorageBlockFunctionHLSL: Wrapper functions for RWByteAddressBuffer Load/Store functions.
//
#include "compiler/translator/ShaderStorageBlockFunctionHLSL.h"
#include "compiler/translator/UtilsHLSL.h"
namespace sh
{
// static
void ShaderStorageBlockFunctionHLSL::OutputSSBOLoadFunctionBody(
TInfoSinkBase &out,
const ShaderStorageBlockFunction &ssboFunction)
{
if (ssboFunction.type.isScalar())
{
TString convertString;
switch (ssboFunction.type.getBasicType())
{
case EbtFloat:
convertString = "asfloat(";
break;
case EbtInt:
convertString = "asint(";
break;
case EbtUInt:
convertString = "asuint(";
break;
case EbtBool:
convertString = "asint(";
break;
default:
UNREACHABLE();
break;
}
out << " " << ssboFunction.typeString << " result = " << convertString
<< "buffer.Load(loc));\n";
out << " return result;\n";
return;
}
// TODO(jiajia.qin@intel.com): Process all possible return types.
out << " return 1.0;\n";
}
// static
void ShaderStorageBlockFunctionHLSL::OutputSSBOStoreFunctionBody(
TInfoSinkBase &out,
const ShaderStorageBlockFunction &ssboFunction)
{
if (ssboFunction.type.isScalar())
{
out << " buffer.Store(loc, asuint(value));\n";
}
// TODO(jiajia.qin@intel.com): Process all possible return types.
}
bool ShaderStorageBlockFunctionHLSL::ShaderStorageBlockFunction::operator<(
const ShaderStorageBlockFunction &rhs) const
{
return std::tie(functionName, typeString, method) <
std::tie(rhs.functionName, rhs.typeString, rhs.method);
}
TString ShaderStorageBlockFunctionHLSL::registerShaderStorageBlockFunction(const TType &type,
SSBOMethod method)
{
ShaderStorageBlockFunction ssboFunction;
ssboFunction.typeString = TypeString(type);
ssboFunction.method = method;
ssboFunction.type = type;
switch (method)
{
case SSBOMethod::LOAD:
ssboFunction.functionName = ssboFunction.typeString + "_Load";
break;
case SSBOMethod::STORE:
ssboFunction.functionName = ssboFunction.typeString + "_Store";
break;
default:
UNREACHABLE();
}
mRegisteredShaderStorageBlockFunctions.insert(ssboFunction);
return ssboFunction.functionName;
}
void ShaderStorageBlockFunctionHLSL::shaderStorageBlockFunctionHeader(TInfoSinkBase &out)
{
for (const ShaderStorageBlockFunction &ssboFunction : mRegisteredShaderStorageBlockFunctions)
{
if (ssboFunction.method == SSBOMethod::LOAD)
{
// Function header
out << ssboFunction.typeString << " " << ssboFunction.functionName
<< "(RWByteAddressBuffer buffer, uint loc)\n";
out << "{\n";
OutputSSBOLoadFunctionBody(out, ssboFunction);
}
else
{
// Function header
out << "void " << ssboFunction.functionName << "(RWByteAddressBuffer buffer, uint loc, "
<< ssboFunction.typeString << " value)\n";
out << "{\n";
OutputSSBOStoreFunctionBody(out, ssboFunction);
}
out << "}\n"
"\n";
}
}
} // namespace sh
//
// Copyright 2018 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.
//
// ShaderStorageBlockOutputHLSL: A traverser to translate a ssbo_access_chain to an offset of
// RWByteAddressBuffer.
// //EOpIndexDirectInterfaceBlock
// ssbo_variable :=
// | the name of the SSBO
// | the name of a variable in an SSBO backed interface block
// // EOpIndexInDirect
// // EOpIndexDirect
// ssbo_array_indexing := ssbo_access_chain[expr_no_ssbo]
// // EOpIndexDirectStruct
// ssbo_structure_access := ssbo_access_chain.identifier
// ssbo_access_chain :=
// | ssbo_variable
// | ssbo_array_indexing
// | ssbo_structure_access
//
#ifndef COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKFUNCTIONHLSL_H_
#define COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKFUNCTIONHLSL_H_
#include <set>
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/Types.h"
namespace sh
{
enum class SSBOMethod
{
LOAD,
STORE
};
class ShaderStorageBlockFunctionHLSL final : angle::NonCopyable
{
public:
TString registerShaderStorageBlockFunction(const TType &type, SSBOMethod method);
void shaderStorageBlockFunctionHeader(TInfoSinkBase &out);
private:
struct ShaderStorageBlockFunction
{
bool operator<(const ShaderStorageBlockFunction &rhs) const;
TString functionName;
TString typeString;
SSBOMethod method;
TType type;
};
static void OutputSSBOLoadFunctionBody(TInfoSinkBase &out,
const ShaderStorageBlockFunction &imageFunction);
static void OutputSSBOStoreFunctionBody(TInfoSinkBase &out,
const ShaderStorageBlockFunction &imageFunction);
using ShaderStorageBlockFunctionSet = std::set<ShaderStorageBlockFunction>;
ShaderStorageBlockFunctionSet mRegisteredShaderStorageBlockFunctions;
};
} // namespace sh
#endif // COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKFUNCTIONHLSL_H_
//
// Copyright 2018 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.
//
// ShaderStorageBlockOutputHLSL: A traverser to translate a buffer variable of shader storage block
// to an offset of RWByteAddressBuffer.
//
#ifndef COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKOUTPUTHLSL_H_
#define COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKOUTPUTHLSL_H_
#include "compiler/translator/ShaderStorageBlockFunctionHLSL.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
namespace sh
{
class ResourcesHLSL;
class OutputHLSL;
class TSymbolTable;
struct TReferencedBlock : angle::NonCopyable
{
POOL_ALLOCATOR_NEW_DELETE();
TReferencedBlock(const TInterfaceBlock *block, const TVariable *instanceVariable);
const TInterfaceBlock *block;
const TVariable *instanceVariable; // May be nullptr if the block is not instanced.
};
// Maps from uniqueId to a variable.
using ReferencedInterfaceBlocks = std::map<int, const TReferencedBlock *>;
class ShaderStorageBlockOutputHLSL : public TIntermTraverser
{
public:
ShaderStorageBlockOutputHLSL(OutputHLSL *outputHLSL,
TSymbolTable *symbolTable,
ResourcesHLSL *resourcesHLSL);
~ShaderStorageBlockOutputHLSL();
// This writes part of the function call to store a value to a SSBO to the output stream. After
// calling this, ", <stored value>)" should be written to the output stream to complete the
// function call.
void outputStoreFunctionCallPrefix(TIntermTyped *node);
// This writes the funciton call to load a SSBO value to the output stream.
void outputLoadFunctionCall(TIntermTyped *node);
void writeShaderStorageBlocksHeader(TInfoSinkBase &out) const;
protected:
void visitSymbol(TIntermSymbol *) override;
void visitConstantUnion(TIntermConstantUnion *) override;
bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
bool visitBinary(Visit visit, TIntermBinary *) override;
private:
void traverseSSBOAccess(TIntermTyped *node, SSBOMethod method);
bool isEndOfSSBOAccessChain();
void writeEOpIndexDirectOrIndirectOutput(TInfoSinkBase &out, Visit visit, TIntermBinary *node);
// Common part in dot operations.
void writeDotOperatorOutput(TInfoSinkBase &out, const TField *field);
bool mIsLoadFunctionCall;
OutputHLSL *mOutputHLSL;
ShaderStorageBlockFunctionHLSL *mSSBOFunctionHLSL;
ResourcesHLSL *mResourcesHLSL;
ReferencedInterfaceBlocks mReferencedShaderStorageBlocks;
};
}
#endif // COMPILER_TRANSLATOR_SHADERSTORAGEBLOCKOUTPUTHLSL_H_
......@@ -174,7 +174,7 @@ TString Std140PaddingHelper::postPaddingString(const TType &type, bool useHLSLRo
return "";
}
int numComponents = 0;
int numComponents = 0;
const TStructure *structure = type.getStruct();
if (type.isMatrix())
......
......@@ -32,7 +32,12 @@ class TField : angle::NonCopyable
public:
POOL_ALLOCATOR_NEW_DELETE();
TField(TType *type, const ImmutableString &name, const TSourceLoc &line, SymbolType symbolType)
: mType(type), mName(name), mLine(line), mSymbolType(symbolType)
: mType(type),
mName(name),
mLine(line),
mSymbolType(symbolType),
mOffset(0),
mArrayStride(0)
{
ASSERT(mSymbolType != SymbolType::Empty);
}
......@@ -44,12 +49,18 @@ class TField : angle::NonCopyable
const ImmutableString &name() const { return mName; }
const TSourceLoc &line() const { return mLine; }
SymbolType symbolType() const { return mSymbolType; }
unsigned int getOffset() const { return mOffset; }
unsigned int getArrayStride() const { return mArrayStride; }
void setOffset(unsigned int offset) { mOffset = offset; }
void setArrayStride(int arrayStride) { mArrayStride = arrayStride; }
private:
TType *mType;
const ImmutableString mName;
const TSourceLoc mLine;
const SymbolType mSymbolType;
unsigned int mOffset;
unsigned int mArrayStride;
};
typedef TVector<TField *> TFieldList;
......
......@@ -3,8 +3,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of vectors and matrices,
// replacing them with calls to functions that choose which component to return or write.
// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of non-SSBO vectors and
// matrices, replacing them with calls to functions that choose which component to return or write.
// We don't need to consider dynamic indexing in SSBO since it can be directly as part of the offset
// of RWByteAddressBuffer.
//
#include "compiler/translator/tree_ops/RemoveDynamicIndexing.h"
......@@ -374,7 +376,7 @@ bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *nod
TIntermSymbol *tempIndex = CreateTempSymbolNode(indexVariable);
queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
}
else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
else if (IntermNodePatternMatcher::IsDynamicIndexingOfNonSSBOVectorOrMatrix(node))
{
mPerfDiagnostics->warning(node->getLine(),
"Performance: dynamic indexing of vectors and "
......@@ -429,7 +431,7 @@ bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *nod
TIntermBinary *leftBinary = node->getLeft()->getAsBinaryNode();
if (leftBinary != nullptr &&
IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(leftBinary))
IntermNodePatternMatcher::IsDynamicIndexingOfNonSSBOVectorOrMatrix(leftBinary))
{
// This is a case like:
// mat2 m;
......
......@@ -3,8 +3,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of vectors and matrices,
// replacing them with calls to functions that choose which component to return or write.
// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of non-SSBO vectors and
// matrices, replacing them with calls to functions that choose which component to return or write.
// We don't need to consider dynamic indexing in SSBO since it can be directly as part of the offset
// of RWByteAddressBuffer.
//
#ifndef COMPILER_TRANSLATOR_TREEOPS_REMOVEDYNAMICINDEXING_H_
......
......@@ -12,6 +12,7 @@
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/util.h"
namespace sh
{
......@@ -48,6 +49,12 @@ IntermNodePatternMatcher::IntermNodePatternMatcher(const unsigned int mask) : mM
}
// static
bool IntermNodePatternMatcher::IsDynamicIndexingOfNonSSBOVectorOrMatrix(TIntermBinary *node)
{
return IsDynamicIndexingOfVectorOrMatrix(node) && !IsInShaderStorageBlock(node->getLeft());
}
// static
bool IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node)
{
return node->getOp() == EOpIndexIndirect && !node->getLeft()->isArray() &&
......
......@@ -24,6 +24,7 @@ class TIntermUnary;
class IntermNodePatternMatcher
{
public:
static bool IsDynamicIndexingOfNonSSBOVectorOrMatrix(TIntermBinary *node);
static bool IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node);
enum PatternType
......
......@@ -751,4 +751,28 @@ bool IsOutputVulkan(ShShaderOutput output)
return output == SH_GLSL_VULKAN_OUTPUT;
}
bool IsInShaderStorageBlock(TIntermTyped *node)
{
TIntermBinary *binaryNode = nullptr;
TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
if (swizzleNode)
{
binaryNode = swizzleNode->getOperand()->getAsBinaryNode();
if (binaryNode)
{
return IsInShaderStorageBlock(binaryNode->getLeft());
}
}
binaryNode = node->getAsBinaryNode();
if (binaryNode)
{
return IsInShaderStorageBlock(binaryNode->getLeft());
}
const TType &type = node->getType();
return type.getQualifier() == EvqBuffer;
}
} // namespace sh
......@@ -25,6 +25,7 @@ namespace sh
{
class TIntermBlock;
class TSymbolTable;
class TIntermTyped;
float NumericLexFloat32OutOfRangeToInfinity(const std::string &str);
......@@ -62,6 +63,8 @@ bool IsOutputESSL(ShShaderOutput output);
bool IsOutputGLSL(ShShaderOutput output);
bool IsOutputHLSL(ShShaderOutput output);
bool IsOutputVulkan(ShShaderOutput output);
bool IsInShaderStorageBlock(TIntermTyped *node);
} // namespace sh
#endif // COMPILER_TRANSLATOR_UTIL_H_
......@@ -58,6 +58,16 @@
1951 D3D11 : dEQP-GLES31.functional.ssbo.* = SKIP
1442 D3D11 : dEQP-GLES31.functional.layout_binding.image.* = SKIP
1951 D3D11 : dEQP-GLES31.functional.shaders.linkage.es31.shader_storage_block.* = SKIP
// This case is skipped since it uses atomic counter builtin functions which haven't been implemented.
1729 D3D11 : dEQP-GLES31.functional.state_query.program.active_atomic_counter_buffers_get_programiv = SKIP
// Below cases are skipped since it uses variable * ssbo.matrix as the expression which haven't been implemented,
// that will result the code entering unreachable.
1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.callbacks.ssbo_block.ssbo_block_interface_matching_tests = SKIP
1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.callbacks.ssbo_block.ssbo_using_shared_qualifier_tests = SKIP
1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.get_error.ssbo_block.ssbo_block_interface_matching_tests = SKIP
1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.get_error.ssbo_block.ssbo_using_shared_qualifier_tests = SKIP
1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.log.ssbo_block.ssbo_block_interface_matching_tests = SKIP
1951 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.log.ssbo_block.ssbo_using_shared_qualifier_tests = SKIP
// D3D11 Failing Tests
1442 D3D11 : dEQP-GLES31.functional.state_query.integer.max_compute_shared_memory_size_* = FAIL
......@@ -80,7 +90,6 @@
1442 D3D11 : dEQP-GLES31.functional.debug.error_groups.case_3 = FAIL
1442 D3D11 : dEQP-GLES31.functional.debug.error_groups.case_9 = FAIL
1442 D3D11 : dEQP-GLES31.functional.debug.error_groups.case_10 = FAIL
1442 D3D11 : dEQP-GLES31.functional.state_query.program.active_atomic_counter_buffers_get_programiv = FAIL
1442 D3D11 : dEQP-GLES31.functional.layout_binding.ubo.* = FAIL
1663 D3D11 : dEQP-GLES31.functional.draw_indirect.compute_interop.* = FAIL
1442 D3D11 : dEQP-GLES31.functional.shaders.builtin_constants.core.max_vertex_attribs = FAIL
......
......@@ -1803,6 +1803,185 @@ TEST_P(ComputeShaderTest, LoadImageThenStore)
EXPECT_EQ(100u, outputValue);
}
// Test that scalar buffer variables are supported.
TEST_P(ComputeShaderTest, ShaderStorageBlocksScalar)
{
const char kCSSource[] =
R"(#version 310 es
layout(local_size_x=1) in;
layout(std140, binding = 0) buffer blockA {
uvec3 uv;
float f;
} instanceA;
layout(std140, binding = 1) buffer blockB {
vec2 v;
uint u[3];
float f;
};
void main()
{
f = instanceA.f;
})";
ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
EXPECT_GL_NO_ERROR();
}
// Test that vector buffer variables are supported.
TEST_P(ComputeShaderTest, ShaderStorageBlocksVector)
{
const char kCSSource[] =
R"(#version 310 es
layout(local_size_x=1) in;
layout(std140, binding = 0) buffer blockA {
vec2 f;
} instanceA;
layout(std140, binding = 1) buffer blockB {
vec3 f;
};
void main()
{
f[1] = instanceA.f[0];
})";
ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
EXPECT_GL_NO_ERROR();
}
// Test that matrix buffer variables are supported.
TEST_P(ComputeShaderTest, ShaderStorageBlocksMatrix)
{
const char kCSSource[] =
R"(#version 310 es
layout(local_size_x=1) in;
layout(std140, binding = 0) buffer blockA {
mat3x4 m;
} instanceA;
layout(std140, binding = 1) buffer blockB {
mat3x4 m;
};
void main()
{
m[0][1] = instanceA.m[0][1];
})";
ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
EXPECT_GL_NO_ERROR();
}
// Test that scalar array buffer variables are supported.
TEST_P(ComputeShaderTest, ShaderStorageBlocksScalarArray)
{
const char kCSSource[] =
R"(#version 310 es
layout(local_size_x=8) in;
layout(std140, binding = 0) buffer blockA {
float f[8];
} instanceA;
layout(std140, binding = 1) buffer blockB {
float f[8];
};
void main()
{
f[gl_LocalInvocationIndex] = instanceA.f[gl_LocalInvocationIndex];
})";
ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
EXPECT_GL_NO_ERROR();
}
// Test that vector array buffer variables are supported.
TEST_P(ComputeShaderTest, ShaderStorageBlocksVectorArray)
{
const char kCSSource[] =
R"(#version 310 es
layout(local_size_x=4) in;
layout(std140, binding = 0) buffer blockA {
vec2 v[4];
} instanceA;
layout(std140, binding = 1) buffer blockB {
vec4 v[4];
};
void main()
{
v[0][gl_LocalInvocationIndex] = instanceA.v[gl_LocalInvocationIndex][1];
})";
ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
EXPECT_GL_NO_ERROR();
}
// Test that matrix array buffer variables are supported.
TEST_P(ComputeShaderTest, ShaderStorageBlocksMatrixArray)
{
const char kCSSource[] =
R"(#version 310 es
layout(local_size_x=8) in;
layout(std140, binding = 0) buffer blockA {
float v1[5];
mat4 m[8];
} instanceA;
layout(std140, binding = 1) buffer blockB {
vec2 v1[3];
mat4 m[8];
};
void main()
{
float data = instanceA.m[gl_LocalInvocationIndex][0][0];
m[gl_LocalInvocationIndex][gl_LocalInvocationIndex][gl_LocalInvocationIndex] = data;
})";
ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
EXPECT_GL_NO_ERROR();
}
// Test that shader storage blocks only in assignment right is supported.
TEST_P(ComputeShaderTest, ShaderStorageBlocksInAssignmentRight)
{
const char kCSSource[] =
R"(#version 310 es
layout(local_size_x=8) in;
layout(std140, binding = 0) buffer blockA {
float data[8];
} instanceA;
layout(r32f, binding = 0) writeonly uniform highp image2D imageOut;
void main()
{
float data = 1.0;
data = instanceA.data[gl_LocalInvocationIndex];
imageStore(imageOut, ivec2(gl_LocalInvocationID.xy), vec4(data));
}
)";
ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
EXPECT_GL_NO_ERROR();
}
// Test that shader storage blocks with unsized array are supported.
TEST_P(ComputeShaderTest, ShaderStorageBlocksWithUnsizedArray)
{
const char kCSSource[] =
R"(#version 310 es
layout(local_size_x=8) in;
layout(std140, binding = 0) buffer blockA {
float v[];
} instanceA;
layout(std140, binding = 0) buffer blockB {
float v[];
} instanceB[1];
void main()
{
float data = instanceA.v[gl_LocalInvocationIndex];
instanceB[0].v[gl_LocalInvocationIndex * 2u + 1u] = data;
}
)";
ANGLE_GL_COMPUTE_PROGRAM(program, kCSSource);
EXPECT_GL_NO_ERROR();
}
// Check that it is not possible to create a compute shader when the context does not support ES
// 3.10
TEST_P(ComputeShaderTestES3, NotSupported)
......
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