Merge pull request #572 from steve-lunarg/numthreads

HLSL: implement numthreads for compute shaders
This commit is contained in:
John Kessenich 2016-11-01 00:25:06 -06:00 committed by GitHub
commit 89df3c2dcb
10 changed files with 351 additions and 23 deletions

View File

@ -0,0 +1,60 @@
hlsl.numthreads.comp
Shader version: 450
local_size = (4, 4, 2)
0:? Sequence
0:4 Function Definition: main(vu3; (temp void)
0:4 Function Parameters:
0:4 'tid' (in 3-component vector of uint)
0:9 Function Definition: main_aux1(vu3; (temp void)
0:9 Function Parameters:
0:9 'tid' (in 3-component vector of uint LocalInvocationID)
0:? Linker Objects
0:? 'tid' (in 3-component vector of uint LocalInvocationID)
Linked compute stage:
Shader version: 450
local_size = (4, 4, 2)
0:? Sequence
0:4 Function Definition: main(vu3; (temp void)
0:4 Function Parameters:
0:4 'tid' (in 3-component vector of uint)
0:9 Function Definition: main_aux1(vu3; (temp void)
0:9 Function Parameters:
0:9 'tid' (in 3-component vector of uint LocalInvocationID)
0:? Linker Objects
0:? 'tid' (in 3-component vector of uint LocalInvocationID)
// Module Version 10000
// Generated by (magic number): 80001
// Id's are bound by 15
Capability Shader
1: ExtInstImport "GLSL.std.450"
MemoryModel Logical GLSL450
EntryPoint GLCompute 4 "main_aux1" 14
ExecutionMode 4 LocalSize 4 4 2
Name 4 "main_aux1"
Name 11 "main(vu3;"
Name 10 "tid"
Name 14 "tid"
Decorate 14(tid) BuiltIn LocalInvocationId
2: TypeVoid
3: TypeFunction 2
6: TypeInt 32 0
7: TypeVector 6(int) 3
8: TypePointer Function 7(ivec3)
9: TypeFunction 2 8(ptr)
13: TypePointer Input 7(ivec3)
14(tid): 13(ptr) Variable Input
4(main_aux1): 2 Function None 3
5: Label
Return
FunctionEnd
11(main(vu3;): 2 Function None 9
10(tid): 8(ptr) FunctionParameter
12: Label
Return
FunctionEnd

14
Test/hlsl.numthreads.comp Normal file
View File

@ -0,0 +1,14 @@
[numthreads(8,8,1)]
void main(uint3 tid : SV_DispatchThreadID )
{
}
[numTHreaDs(4,4,2)] // case insensitive
void main_aux1(uint3 tid : SV_DispatchThreadID )
{
}
[numthreads(1,4,8)]
void main_aux2(uint3 tid : SV_DispatchThreadID );

View File

@ -151,6 +151,7 @@ INSTANTIATE_TEST_CASE_P(
{"hlsl.multiReturn.frag", "main"},
{"hlsl.matrixindex.frag", "main"},
{"hlsl.numericsuffixes.frag", "main"},
{"hlsl.numthreads.comp", "main_aux1"},
{"hlsl.overload.frag", "PixelShaderFunction"},
{"hlsl.pp.line.frag", "main"},
{"hlsl.precise.frag", "main"},

View File

@ -1,4 +1,5 @@
set(SOURCES
hlslAttributes.cpp
hlslParseHelper.cpp
hlslScanContext.cpp
hlslOpMap.cpp
@ -7,6 +8,7 @@ set(SOURCES
hlslParseables.cpp)
set(HEADERS
hlslAttributes.h
hlslParseHelper.h
hlslTokens.h
hlslScanContext.h

108
hlsl/hlslAttributes.cpp Normal file
View File

@ -0,0 +1,108 @@
//
//Copyright (C) 2016 LunarG, Inc.
//
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of Google, Inc., nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//POSSIBILITY OF SUCH DAMAGE.
//
#include "hlslAttributes.h"
#include <cstdlib>
#include <cctype>
namespace glslang {
// Map the given string to an attribute enum from TAttributeType,
// or EatNone if invalid.
TAttributeType TAttributeMap::attributeFromName(const TString& name)
{
// These are case insensitive.
TString lowername(name);
std::transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower);
if (lowername == "allow_uav_condition")
return EatAllow_uav_condition;
else if (lowername == "branch")
return EatBranch;
else if (lowername == "call")
return EatCall;
else if (lowername == "domain")
return EatDomain;
else if (lowername == "earlydepthstencil")
return EatEarlydepthstencil;
else if (lowername == "fastopt")
return EatFastopt;
else if (lowername == "flatten")
return EatFlatten;
else if (lowername == "forcecase")
return EatForcecase;
else if (lowername == "instance")
return EatInstance;
else if (lowername == "maxtessfactor")
return EatMaxtessfactor;
else if (lowername == "numthreads")
return EatNumthreads;
else if (lowername == "outputcontrolpoints")
return EatOutputcontrolpoints;
else if (lowername == "outputtopology")
return EatOutputtopology;
else if (lowername == "partitioning")
return EatPartitioning;
else if (lowername == "patchconstantfunc")
return EatPatchconstantfunc;
else if (lowername == "unroll")
return EatUnroll;
else
return EatNone;
}
// Look up entry, inserting if it's not there, and if name is a valid attribute name
// as known by attributeFromName.
TAttributeType TAttributeMap::setAttribute(const TString* name, TIntermAggregate* value)
{
if (name == nullptr)
return EatNone;
const TAttributeType attr = attributeFromName(*name);
if (attr != EatNone)
attributes[attr] = value;
return attr;
}
// Look up entry (const version), and return aggregate node. This cannot change the map.
const TIntermAggregate* TAttributeMap::operator[](TAttributeType attr) const
{
const auto entry = attributes.find(attr);
return (entry == attributes.end()) ? nullptr : entry->second;
}
} // end namespace glslang

96
hlsl/hlslAttributes.h Normal file
View File

@ -0,0 +1,96 @@
//
//Copyright (C) 2016 LunarG, Inc.
//
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of Google, Inc., nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//POSSIBILITY OF SUCH DAMAGE.
//
#ifndef HLSLATTRIBUTES_H_
#define HLSLATTRIBUTES_H_
#include <unordered_map>
#include <functional>
#include "hlslScanContext.h"
#include "../glslang/Include/Common.h"
namespace glslang {
enum TAttributeType {
EatNone,
EatAllow_uav_condition,
EatBranch,
EatCall,
EatDomain,
EatEarlydepthstencil,
EatFastopt,
EatFlatten,
EatForcecase,
EatInstance,
EatMaxtessfactor,
EatNumthreads,
EatOutputcontrolpoints,
EatOutputtopology,
EatPartitioning,
EatPatchconstantfunc,
EatUnroll,
};
}
namespace std {
// Allow use of TAttributeType enum in hash_map without calling code having to cast.
template <> struct hash<glslang::TAttributeType> {
std::size_t operator()(glslang::TAttributeType attr) const {
return std::hash<int>()(int(attr));
}
};
} // end namespace std
namespace glslang {
class TIntermAggregate;
class TAttributeMap {
public:
// Search for and potentially add the attribute into the map. Return the
// attribute type enum for it, if found, else EatNone.
TAttributeType setAttribute(const TString* name, TIntermAggregate* value);
// Const lookup: search for (but do not modify) the attribute in the map.
const TIntermAggregate* operator[](TAttributeType) const;
protected:
// Find an attribute enum given its name.
static TAttributeType attributeFromName(const TString&);
std::unordered_map<TAttributeType, TIntermAggregate*> attributes;
};
} // end namespace glslang
#endif // HLSLATTRIBUTES_H_

View File

@ -53,6 +53,7 @@
#include "hlslTokens.h"
#include "hlslGrammar.h"
#include "hlslAttributes.h"
namespace glslang {
@ -268,6 +269,10 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& node)
node = nullptr;
bool list = false;
// attributes
TAttributeMap attributes;
acceptAttributes(attributes);
// typedef
bool typedefDecl = acceptTokenClass(EHTokTypedef);
@ -302,7 +307,7 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& node)
parseContext.error(idToken.loc, "function body can't be in a declarator list", "{", "");
if (typedefDecl)
parseContext.error(idToken.loc, "function body can't be in a typedef", "{", "");
return acceptFunctionDefinition(function, node);
return acceptFunctionDefinition(function, node, attributes);
} else {
if (typedefDecl)
parseContext.error(idToken.loc, "function typedefs not implemented", "{", "");
@ -1687,13 +1692,13 @@ bool HlslGrammar::acceptParameterDeclaration(TFunction& function)
// Do the work to create the function definition in addition to
// parsing the body (compound_statement).
bool HlslGrammar::acceptFunctionDefinition(TFunction& function, TIntermNode*& node)
bool HlslGrammar::acceptFunctionDefinition(TFunction& function, TIntermNode*& node, const TAttributeMap& attributes)
{
TFunction& functionDeclarator = parseContext.handleFunctionDeclarator(token.loc, function, false /* not prototype */);
TSourceLoc loc = token.loc;
// This does a pushScope()
node = parseContext.handleFunctionDefinition(loc, functionDeclarator);
node = parseContext.handleFunctionDefinition(loc, functionDeclarator, attributes);
// compound_statement
TIntermNode* functionBody = nullptr;
@ -2344,7 +2349,8 @@ bool HlslGrammar::acceptStatement(TIntermNode*& statement)
statement = nullptr;
// attributes
acceptAttributes();
TAttributeMap attributes;
acceptAttributes(attributes);
// attributed_statement
switch (peek()) {
@ -2417,42 +2423,68 @@ bool HlslGrammar::acceptStatement(TIntermNode*& statement)
// | FLATTEN
// | FORCECASE
// | CALL
// | DOMAIN
// | EARLYDEPTHSTENCIL
// | INSTANCE
// | MAXTESSFACTOR
// | OUTPUTCONTROLPOINTS
// | OUTPUTTOPOLOGY
// | PARTITIONING
// | PATCHCONSTANTFUNC
// | NUMTHREADS LEFT_PAREN x_size, y_size,z z_size RIGHT_PAREN
//
void HlslGrammar::acceptAttributes()
void HlslGrammar::acceptAttributes(TAttributeMap& attributes)
{
// For now, accept the [ XXX(X) ] syntax, but drop.
// For now, accept the [ XXX(X) ] syntax, but drop all but
// numthreads, which is used to set the CS local size.
// TODO: subset to correct set? Pass on?
do {
HlslToken idToken;
// LEFT_BRACKET?
if (! acceptTokenClass(EHTokLeftBracket))
return;
// attribute
if (peekTokenClass(EHTokIdentifier)) {
// 'token.string' is the attribute
advanceToken();
if (acceptIdentifier(idToken)) {
// 'idToken.string' is the attribute
} else if (! peekTokenClass(EHTokRightBracket)) {
expected("identifier");
advanceToken();
}
// (x)
TIntermAggregate* literals = nullptr;
// (x, ...)
if (acceptTokenClass(EHTokLeftParen)) {
literals = new TIntermAggregate;
TIntermTyped* node;
if (! acceptLiteral(node))
expected("literal");
// 'node' has the literal in it
bool expectingLiteral = false;
while (acceptLiteral(node)) {
expectingLiteral = false;
literals->getSequence().push_back(node);
if (acceptTokenClass(EHTokComma))
expectingLiteral = true;
}
// 'literals' is an aggregate with the literals in it
if (! acceptTokenClass(EHTokRightParen))
expected(")");
if (expectingLiteral || literals->getSequence().empty())
expected("literal");
}
// RIGHT_BRACKET
if (acceptTokenClass(EHTokRightBracket))
continue;
if (!acceptTokenClass(EHTokRightBracket)) {
expected("]");
return;
}
// Add any values we found into the attribute map. This accepts
// (and ignores) values not mapping to a known TAttributeType;
attributes.setAttribute(idToken.string, literals);
} while (true);
}

View File

@ -43,6 +43,8 @@
namespace glslang {
class TAttributeMap; // forward declare
// Should just be the grammar aspect of HLSL.
// Described in more detail in hlslGrammar.cpp.
@ -80,7 +82,7 @@ namespace glslang {
bool acceptStructDeclarationList(TTypeList*&);
bool acceptFunctionParameters(TFunction&);
bool acceptParameterDeclaration(TFunction&);
bool acceptFunctionDefinition(TFunction&, TIntermNode*&);
bool acceptFunctionDefinition(TFunction&, TIntermNode*&, const TAttributeMap&);
bool acceptParenExpression(TIntermTyped*&);
bool acceptExpression(TIntermTyped*&);
bool acceptInitializer(TIntermTyped*&);
@ -98,7 +100,7 @@ namespace glslang {
bool acceptScopedStatement(TIntermNode*&);
bool acceptScopedCompoundStatement(TIntermNode*&);
bool acceptNestedStatement(TIntermNode*&);
void acceptAttributes();
void acceptAttributes(TAttributeMap&);
bool acceptSelectionStatement(TIntermNode*&);
bool acceptSwitchStatement(TIntermNode*&);
bool acceptIterationStatement(TIntermNode*&);

View File

@ -37,6 +37,7 @@
#include "hlslParseHelper.h"
#include "hlslScanContext.h"
#include "hlslGrammar.h"
#include "hlslAttributes.h"
#include "../glslang/MachineIndependent/Scan.h"
#include "../glslang/MachineIndependent/preprocessor/PpContext.h"
@ -1045,7 +1046,8 @@ TFunction& HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFu
// Handle seeing the function prototype in front of a function definition in the grammar.
// The body is handled after this function returns.
//
TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function)
TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function,
const TAttributeMap& attributes)
{
currentCaller = function.getMangledName();
TSymbol* symbol = symbolTable.find(function.getMangledName());
@ -1134,6 +1136,15 @@ TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& l
controlFlowNestingLevel = 0;
postMainReturn = false;
// Handle function attributes
const TIntermAggregate* numThreadliterals = attributes[EatNumthreads];
if (numThreadliterals != nullptr && inEntryPoint) {
const TIntermSequence& sequence = numThreadliterals->getSequence();
for (int lid = 0; lid < int(sequence.size()); ++lid)
intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
}
return paramNodes;
}

View File

@ -41,6 +41,8 @@
namespace glslang {
class TAttributeMap; // forward declare
class HlslParseContext : public TParseContextBase {
public:
HlslParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins,
@ -69,7 +71,7 @@ public:
TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field);
void assignLocations(TVariable& variable);
TFunction& handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&);
TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributeMap&);
void handleFunctionBody(const TSourceLoc&, TFunction&, TIntermNode* functionBody, TIntermNode*& node);
void remapEntryPointIO(TFunction& function);
void remapNonEntryPointIO(TFunction& function);