Commit aa2626f9 by Shahbaz Youssefi Committed by Angle LUCI CQ

Vulkan: SPIR-V Gen: Handle load/store access chain

This change implements a key instruction of SPIR-V, OpAccessChain. Inspired by glslang's implementation, a class is added (AccessChain) that tracks "indices" into a base value. These indices could select a field of a block, an element of an array, a column of a matrix, or a component of a vector. Nuances (such as multi-component swizzle as an lvalue not representable in SPIR-V) and optimizations (such as all-literal indices to an rvalue) that are implemented in glslang have also been implemented in this change. As a result, this change implements all manners of loads and stores (with the exception of this gotcha: https://github.com/KhronosGroup/glslang/issues/2637). The change uses this feature to translate the basic shader which does `gl_Position = positionAttr;` (by implementing EOpAssign), and enables a test that uses this shader. Bug: angleproject:4889 Change-Id: I22dbe5b169ce499eaac657902164aca3b0ebc193 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2911880 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent 152450f8
...@@ -1086,6 +1086,11 @@ inline bool IsShaderOut(TQualifier qualifier) ...@@ -1086,6 +1086,11 @@ inline bool IsShaderOut(TQualifier qualifier)
case EvqSampleOut: case EvqSampleOut:
case EvqPatchOut: case EvqPatchOut:
case EvqFragmentInOut: case EvqFragmentInOut:
// Per-vertex built-ins when used without gl_in or gl_out are always output.
case EvqPosition:
case EvqPointSize:
case EvqClipDistance:
case EvqCullDistance:
return true; return true;
default: default:
return false; return false;
......
...@@ -293,7 +293,7 @@ SpirvTypeData SPIRVBuilder::declareType(const SpirvType &type, const char *block ...@@ -293,7 +293,7 @@ SpirvTypeData SPIRVBuilder::declareType(const SpirvType &type, const char *block
// unconditionally and having the SPIR-V transformer remove them, it's better to avoid // unconditionally and having the SPIR-V transformer remove them, it's better to avoid
// generating them in the first place. This both simplifies the transformer and reduces SPIR-V // generating them in the first place. This both simplifies the transformer and reduces SPIR-V
// binary size that gets written to disk cache. http://anglebug.com/4889 // binary size that gets written to disk cache. http://anglebug.com/4889
if (type.block != nullptr) if (type.block != nullptr && type.arraySizes.empty())
{ {
spirv::WriteName(&mSpirvDebug, typeId, blockName); spirv::WriteName(&mSpirvDebug, typeId, blockName);
...@@ -912,6 +912,10 @@ uint32_t SPIRVBuilder::calculateBaseAlignmentAndSize(const SpirvType &type, ...@@ -912,6 +912,10 @@ uint32_t SPIRVBuilder::calculateBaseAlignmentAndSize(const SpirvType &type,
// > laid out in order, according to rule (9). // > laid out in order, according to rule (9).
SpirvType baseType = type; SpirvType baseType = type;
baseType.arraySizes = {}; baseType.arraySizes = {};
if (baseType.arraySizes.empty() && baseType.block == nullptr)
{
baseType.blockStorage = EbsUnspecified;
}
const SpirvTypeData &baseTypeData = getSpirvTypeData(baseType, ""); const SpirvTypeData &baseTypeData = getSpirvTypeData(baseType, "");
uint32_t baseAlignment = baseTypeData.baseAlignment; uint32_t baseAlignment = baseTypeData.baseAlignment;
...@@ -1173,6 +1177,12 @@ spirv::Blob SPIRVBuilder::getSpirv() ...@@ -1173,6 +1177,12 @@ spirv::Blob SPIRVBuilder::getSpirv()
} }
result.insert(result.end(), mSpirvExecutionModes.begin(), mSpirvExecutionModes.end()); result.insert(result.end(), mSpirvExecutionModes.begin(), mSpirvExecutionModes.end());
// - OpSource instruction.
//
// This is to support debuggers and capture/replay tools and isn't strictly necessary.
spirv::WriteSource(&result, spv::SourceLanguageGLSL, spirv::LiteralInteger(450), nullptr,
nullptr);
// Append the already generated sections in order // Append the already generated sections in order
result.insert(result.end(), mSpirvDebug.begin(), mSpirvDebug.end()); result.insert(result.end(), mSpirvDebug.begin(), mSpirvDebug.end());
result.insert(result.end(), mSpirvDecorations.begin(), mSpirvDecorations.end()); result.insert(result.end(), mSpirvDecorations.begin(), mSpirvDecorations.end());
......
...@@ -177,6 +177,8 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -177,6 +177,8 @@ class SPIRVBuilder : angle::NonCopyable
spirv::Blob *getSpirvExecutionModes() { return &mSpirvExecutionModes; } spirv::Blob *getSpirvExecutionModes() { return &mSpirvExecutionModes; }
spirv::Blob *getSpirvDebug() { return &mSpirvDebug; } spirv::Blob *getSpirvDebug() { return &mSpirvDebug; }
spirv::Blob *getSpirvDecorations() { return &mSpirvDecorations; } spirv::Blob *getSpirvDecorations() { return &mSpirvDecorations; }
spirv::Blob *getSpirvTypeAndConstantDecls() { return &mSpirvTypeAndConstantDecls; }
spirv::Blob *getSpirvTypePointerDecls() { return &mSpirvTypePointerDecls; }
spirv::Blob *getSpirvVariableDecls() { return &mSpirvVariableDecls; } spirv::Blob *getSpirvVariableDecls() { return &mSpirvVariableDecls; }
spirv::Blob *getSpirvFunctions() { return &mSpirvFunctions; } spirv::Blob *getSpirvFunctions() { return &mSpirvFunctions; }
...@@ -190,6 +192,12 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -190,6 +192,12 @@ class SPIRVBuilder : angle::NonCopyable
uint32_t calculateBaseAlignmentAndSize(const SpirvType &type, uint32_t *sizeInStorageBlockOut); uint32_t calculateBaseAlignmentAndSize(const SpirvType &type, uint32_t *sizeInStorageBlockOut);
uint32_t calculateSizeAndWriteOffsetDecorations(const SpirvType &type, spirv::IdRef typeId); uint32_t calculateSizeAndWriteOffsetDecorations(const SpirvType &type, spirv::IdRef typeId);
spirv::IdRef getBoolConstant(bool value);
spirv::IdRef getUintConstant(uint32_t value);
spirv::IdRef getIntConstant(int32_t value);
spirv::IdRef getFloatConstant(float value);
spirv::IdRef getCompositeConstant(spirv::IdRef typeId, const spirv::IdRefList &values);
// TODO: remove name hashing once translation through glslang is removed. That is necessary to // TODO: remove name hashing once translation through glslang is removed. That is necessary to
// avoid name collision between ANGLE's internal symbols and user-defined ones when compiling // avoid name collision between ANGLE's internal symbols and user-defined ones when compiling
// the generated GLSL, but is irrelevant when generating SPIR-V directly. Currently, the SPIR-V // the generated GLSL, but is irrelevant when generating SPIR-V directly. Currently, the SPIR-V
...@@ -215,14 +223,9 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -215,14 +223,9 @@ class SPIRVBuilder : angle::NonCopyable
spirv::LiteralInteger *sampledOut); spirv::LiteralInteger *sampledOut);
spv::ImageFormat getImageFormat(TLayoutImageInternalFormat imageInternalFormat); spv::ImageFormat getImageFormat(TLayoutImageInternalFormat imageInternalFormat);
spirv::IdRef getBoolConstant(bool value);
spirv::IdRef getBasicConstantHelper(uint32_t value, spirv::IdRef getBasicConstantHelper(uint32_t value,
TBasicType type, TBasicType type,
angle::HashMap<uint32_t, spirv::IdRef> *constants); angle::HashMap<uint32_t, spirv::IdRef> *constants);
spirv::IdRef getUintConstant(uint32_t value);
spirv::IdRef getIntConstant(int32_t value);
spirv::IdRef getFloatConstant(float value);
spirv::IdRef getCompositeConstant(spirv::IdRef typeId, const spirv::IdRefList &values);
uint32_t nextUnusedBinding(); uint32_t nextUnusedBinding();
uint32_t nextUnusedInputLocation(uint32_t consumedCount); uint32_t nextUnusedInputLocation(uint32_t consumedCount);
......
...@@ -1340,7 +1340,10 @@ bool TranslatorVulkan::translate(TIntermBlock *root, ...@@ -1340,7 +1340,10 @@ bool TranslatorVulkan::translate(TIntermBlock *root,
} }
#if defined(ANGLE_ENABLE_DIRECT_SPIRV_GENERATION) #if defined(ANGLE_ENABLE_DIRECT_SPIRV_GENERATION)
if ((compileOptions & SH_GENERATE_SPIRV_DIRECTLY) != 0 && getShaderType() == GL_VERTEX_SHADER) constexpr ShCompileOptions kUnsupportedTransformations =
SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE | SH_ADD_BRESENHAM_LINE_RASTER_EMULATION;
if ((compileOptions & SH_GENERATE_SPIRV_DIRECTLY) != 0 && getShaderType() == GL_VERTEX_SHADER &&
(compileOptions & kUnsupportedTransformations) == 0)
{ {
// Declare the implicitly defined gl_PerVertex I/O blocks if not already. This will help // Declare the implicitly defined gl_PerVertex I/O blocks if not already. This will help
// SPIR-V generation treat them mostly like usual I/O blocks. // SPIR-V generation treat them mostly like usual I/O blocks.
......
...@@ -73,6 +73,6 @@ TEST_P(ColorMaskTest, AMDZeroColorMaskBug) ...@@ -73,6 +73,6 @@ TEST_P(ColorMaskTest, AMDZeroColorMaskBug)
// Use this to select which configurations (e.g. which renderer, which GLES major version) these // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. D3D11 Feature Level 9_3 uses different D3D formats for vertex // tests should be run against. D3D11 Feature Level 9_3 uses different D3D formats for vertex
// attribs compared to Feature Levels 10_0+, so we should test them separately. // attribs compared to Feature Levels 10_0+, so we should test them separately.
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ColorMaskTest); ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(ColorMaskTest, WithDirectSPIRVGeneration(ES2_VULKAN()));
} // namespace angle } // namespace angle
...@@ -515,7 +515,7 @@ class GLSLTest_ES3 : public GLSLTest ...@@ -515,7 +515,7 @@ class GLSLTest_ES3 : public GLSLTest
class GLSLTest_ES31 : public GLSLTest class GLSLTest_ES31 : public GLSLTest
{}; {};
std::string BuillBigInitialStackShader(int length) std::string BuildBigInitialStackShader(int length)
{ {
std::string result; std::string result;
result += "void main() { \n"; result += "void main() { \n";
...@@ -1482,6 +1482,25 @@ TEST_P(GLSLTest_ES3, MissingReturnStructOfArrays) ...@@ -1482,6 +1482,25 @@ TEST_P(GLSLTest_ES3, MissingReturnStructOfArrays)
EXPECT_NE(0u, program); EXPECT_NE(0u, program);
} }
// Verify that non-const index used on an array returned by a function compiles
TEST_P(GLSLTest_ES3, ReturnArrayOfStructsThenNonConstIndex)
{
constexpr char kVS[] = R"(#version 300 es
in float v_varying;
struct s { float a; int b; vec2 c; };
s[2] f()
{
return s[2](s(v_varying, 1, vec2(1.0, 1.0)), s(v_varying / 2.0, 1, vec2(1.0, 1.0)));
}
void main()
{
gl_Position = vec4(f()[uint(v_varying)].a, 0, 0, 1);
})";
GLuint program = CompileProgram(kVS, essl3_shaders::fs::Red());
EXPECT_NE(0u, program);
}
// Verify that using invariant(all) in both shaders fails in ESSL 3.00. // Verify that using invariant(all) in both shaders fails in ESSL 3.00.
TEST_P(GLSLTest_ES3, InvariantAllBoth) TEST_P(GLSLTest_ES3, InvariantAllBoth)
{ {
...@@ -8178,7 +8197,7 @@ TEST_P(GLSLTest, MemoryExhaustedTest) ...@@ -8178,7 +8197,7 @@ TEST_P(GLSLTest, MemoryExhaustedTest)
{ {
ANGLE_SKIP_TEST_IF(IsD3D11_FL93()); ANGLE_SKIP_TEST_IF(IsD3D11_FL93());
GLuint program = GLuint program =
CompileProgram(essl1_shaders::vs::Simple(), BuillBigInitialStackShader(36).c_str()); CompileProgram(essl1_shaders::vs::Simple(), BuildBigInitialStackShader(36).c_str());
EXPECT_NE(0u, program); EXPECT_NE(0u, program);
} }
......
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