Add support for GL_EXT_shared_memory_block

Uses SPV_KHR_workgroup_memory_explicit_layout.  Note that if
GL_EXT_scalar_block_layout is enabled, Workgroup blocks can also use
scalar layout.
This commit is contained in:
Caio Marcelo de Oliveira Filho 2020-06-02 16:58:51 -07:00
parent 3785ddd59a
commit 4bfbf62794
33 changed files with 822 additions and 8 deletions

View file

@ -87,6 +87,10 @@ TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, b
globalInputDefaults.clear();
globalOutputDefaults.clear();
globalSharedDefaults.clear();
globalSharedDefaults.layoutMatrix = ElmColumnMajor;
globalSharedDefaults.layoutPacking = ElpStd430;
#ifndef GLSLANG_WEB
// "Shaders in the transform
// feedback capturing mode have an initial global default of
@ -6033,12 +6037,28 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type)
}
}
static bool storageCanHaveLayoutInBlock(const enum TStorageQualifier storage)
{
switch (storage) {
case EvqUniform:
case EvqBuffer:
case EvqShared:
return true;
default:
return false;
}
}
// Do layout error checking that can be done within a layout qualifier proper, not needing to know
// if there are blocks, atomic counters, variables, etc.
void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier)
{
if (qualifier.storage == EvqShared && qualifier.hasLayout())
error(loc, "cannot apply layout qualifiers to a shared variable", "shared", "");
if (qualifier.storage == EvqShared && qualifier.hasLayout()) {
if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4) {
error(loc, "shared block requires at least SPIR-V 1.4", "shared block", "");
}
profileRequires(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, 0, E_GL_EXT_shared_memory_block, "shared block");
}
// "It is a compile-time error to use *component* without also specifying the location qualifier (order does not matter)."
if (qualifier.hasComponent() && ! qualifier.hasLocation())
@ -6121,7 +6141,7 @@ void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier
error(loc, "can only be used on an output", "xfb layout qualifier", "");
}
if (qualifier.hasUniformLayout()) {
if (! qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory()) {
if (!storageCanHaveLayoutInBlock(qualifier.storage) && !qualifier.isTaskMemory()) {
if (qualifier.hasMatrix() || qualifier.hasPacking())
error(loc, "matrix or packing qualifiers can only be used on a uniform or buffer", "layout", "");
if (qualifier.hasOffset() || qualifier.hasAlign())
@ -7667,6 +7687,7 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con
case EvqBuffer: defaultQualification = globalBufferDefaults; break;
case EvqVaryingIn: defaultQualification = globalInputDefaults; break;
case EvqVaryingOut: defaultQualification = globalOutputDefaults; break;
case EvqShared: defaultQualification = globalSharedDefaults; break;
default: defaultQualification.clear(); break;
}
@ -7930,6 +7951,12 @@ void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& q
error(loc, "output blocks cannot be used in a task shader", "out", "");
}
break;
case EvqShared:
if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4) {
error(loc, "shared block requires at least SPIR-V 1.4", "shared block", "");
}
profileRequires(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, 0, E_GL_EXT_shared_memory_block, "shared block");
break;
#ifndef GLSLANG_WEB
case EvqPayload:
profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "rayPayloadNV block");
@ -8088,7 +8115,7 @@ void TParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList)
//
void TParseContext::fixBlockUniformOffsets(TQualifier& qualifier, TTypeList& typeList)
{
if (!qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory())
if (!storageCanHaveLayoutInBlock(qualifier.storage) && !qualifier.isTaskMemory())
return;
if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430 && qualifier.layoutPacking != ElpScalar)
return;
@ -8602,8 +8629,14 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con
}
#endif
break;
case EvqShared:
if (qualifier.hasMatrix())
globalSharedDefaults.layoutMatrix = qualifier.layoutMatrix;
if (qualifier.hasPacking())
globalSharedDefaults.layoutPacking = qualifier.layoutPacking;
break;
default:
error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", "");
error(loc, "default qualifier requires 'uniform', 'buffer', 'in', 'out' or 'shared' storage qualification", "", "");
return;
}

View file

@ -485,6 +485,7 @@ protected:
TQualifier globalUniformDefaults;
TQualifier globalInputDefaults;
TQualifier globalOutputDefaults;
TQualifier globalSharedDefaults;
TString currentCaller; // name of last function body entered (not valid when at global scope)
#ifndef GLSLANG_WEB
int* atomicUintOffsets; // to become an array of the right size to hold an offset per binding point

View file

@ -331,6 +331,7 @@ void TParseVersions::initializeExtensionBehavior()
extensionBehavior[E_GL_EXT_fragment_shading_rate] = EBhDisable;
extensionBehavior[E_GL_EXT_shader_image_int64] = EBhDisable;
extensionBehavior[E_GL_EXT_terminate_invocation] = EBhDisable;
extensionBehavior[E_GL_EXT_shared_memory_block] = EBhDisable;
// OVR extensions
extensionBehavior[E_GL_OVR_multiview] = EBhDisable;
@ -472,6 +473,7 @@ void TParseVersions::getPreamble(std::string& preamble)
"#define GL_EXT_demote_to_helper_invocation 1\n"
"#define GL_EXT_debug_printf 1\n"
"#define GL_EXT_fragment_shading_rate 1\n"
"#define GL_EXT_shared_memory_block 1\n"
// GL_KHR_shader_subgroup
"#define GL_KHR_shader_subgroup_basic 1\n"

View file

@ -202,6 +202,7 @@ const char* const E_GL_EXT_shader_implicit_conversions = "GL_EXT_shader_imp
const char* const E_GL_EXT_fragment_shading_rate = "GL_EXT_fragment_shading_rate";
const char* const E_GL_EXT_shader_image_int64 = "GL_EXT_shader_image_int64";
const char* const E_GL_EXT_null_initializer = "GL_EXT_null_initializer";
const char* const E_GL_EXT_shared_memory_block = "GL_EXT_shared_memory_block";
// Arrays of extensions for the above viewportEXTs duplications

View file

@ -653,6 +653,25 @@ void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& sy
#endif
}
void TIntermediate::sharedBlockCheck(TInfoSink& infoSink)
{
bool has_shared_block = false;
bool has_shared_non_block = false;
TIntermSequence& linkObjects = findLinkerObjects()->getSequence();
for (size_t i = 0; i < linkObjects.size(); ++i) {
const TType& type = linkObjects[i]->getAsTyped()->getType();
const TQualifier& qualifier = type.getQualifier();
if (qualifier.storage == glslang::EvqShared) {
if (type.getBasicType() == glslang::EbtBlock)
has_shared_block = true;
else
has_shared_non_block = true;
}
}
if (has_shared_block && has_shared_non_block)
error(infoSink, "cannot mix use of shared variables inside and outside blocks");
}
//
// Do final link-time error checking of a complete (merged) intermediate representation.
// (Much error checking was done during merging).
@ -778,6 +797,7 @@ void TIntermediate::finalCheck(TInfoSink& infoSink, bool keepUncalled)
error(infoSink, "post_depth_coverage requires early_fragment_tests");
break;
case EShLangCompute:
sharedBlockCheck(infoSink);
break;
case EShLangRayGen:
case EShLangIntersect:
@ -810,6 +830,7 @@ void TIntermediate::finalCheck(TInfoSink& infoSink, bool keepUncalled)
case EShLangTaskNV:
if (numTaskNVBlocks > 1)
error(infoSink, "Only one taskNV interface block is allowed per shader");
sharedBlockCheck(infoSink);
break;
default:
error(infoSink, "Unknown Stage.");

View file

@ -954,6 +954,7 @@ protected:
void checkCallGraphCycles(TInfoSink&);
void checkCallGraphBodies(TInfoSink&, bool keepUncalled);
void inOutLocationCheck(TInfoSink&);
void sharedBlockCheck(TInfoSink&);
bool userOutputUsed() const;
bool isSpecializationOperation(const TIntermOperator&) const;
bool isNonuniformPropagating(TOperator) const;