summaryrefslogtreecommitdiffstats
path: root/tools/render-test
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2021-03-30 08:38:33 -0700
committerGitHub <noreply@github.com>2021-03-30 08:38:33 -0700
commit488e7cd8d02bf9e1d37b3e76fad6cb5ced6d8878 (patch)
tree28a0d5a8502199541ec2e7ff1d9d798c5fe011e0 /tools/render-test
parent129faf8c4af4a57b7f1c71749f45b31aebfab038 (diff)
Add a streamlined syntax for TEST_INPUT lines (#1768)
This change allows the `TEST_INPUT` syntax used by `render-test` to support aggregate values with a single input line more easily. The test writer can now use a syntax like: ``` //TEST_INPUT:set someVar = 3.0 ``` Input lines that start with the `set` keyword will now use a simpler `dst = src` format (instead of `dst:name=src` as the existing syntax used). The right-hand side expression can include: * Numeric literals, both integer and floating-point (currently only supporting 32-bit scalar types; we could fix this later) * Arrays, consisting of zero or more comma-separated expressions inside `[]` * Aggregates, consisting of zero or more comma-separated "fields" inside `{}`. A field can either be `name: <expr>` or just `<expr>` * Objects, which can be written as either `new SomeType{ <fields> }` or `new{ <fields> }` in the case where the type is know-able from context With this approach is should be possible to support almost arbitrary-type inputs on a single line. For now, I have used this support to re-enable an existing test that had been disabled due to lack of support for setting up arrays of objects. Major things left to do: * The new syntax doesn't support the existing cases we had for `Texture2D`, etc. Those should probably be supported but I'd like to find a way to do it without duplicating the parsing logic (ideally the value cases from the existing code should Just Work in the new model) * There is no support right now for non-32-bit scalar types * It would be good if this support (and the shader cursor system) supported treating vectors like aggregates * The actual value-setting logic doesn't currently handle aggregates without field names, so `{ a:0, b:1 }` will work but `{ 0, 1 }` will parse but fail when it comes time to set values * While this approach lets complicated values be set with a single line, that isn't always what a user will want to do: in the future we should provide a way to break up an aggregate value over multiple lines that is consistent with this approach * Once we port all of the relvant tests over, it would be great to drop the `set` prefix and have these lines look as simple and conventional as possible
Diffstat (limited to 'tools/render-test')
-rw-r--r--tools/render-test/render-test-main.cpp14
-rw-r--r--tools/render-test/shader-input-layout.cpp232
2 files changed, 205 insertions, 41 deletions
diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp
index 325f12e49..87d9a246d 100644
--- a/tools/render-test/render-test-main.cpp
+++ b/tools/render-test/render-test-main.cpp
@@ -334,6 +334,17 @@ struct AssignValsFromLayoutContext
return SLANG_OK;
}
+ SlangResult assignArray(ShaderCursor const& dstCursor, ShaderInputLayout::ArrayVal* srcVal)
+ {
+ Index elementCounter = 0;
+ for(auto elementVal : srcVal->vals)
+ {
+ Index elementIndex = elementCounter++;
+ SLANG_RETURN_ON_FAIL(assign(dstCursor[elementIndex], elementVal));
+ }
+ return SLANG_OK;
+ }
+
SlangResult assign(ShaderCursor const& dstCursor, ShaderInputLayout::ValPtr const& srcVal)
{
auto& entryCursor = dstCursor;
@@ -360,6 +371,9 @@ struct AssignValsFromLayoutContext
case ShaderInputType::Aggregate:
return assignAggregate(dstCursor, (ShaderInputLayout::AggVal*) srcVal.Ptr());
+ case ShaderInputType::Array:
+ return assignArray(dstCursor, (ShaderInputLayout::ArrayVal*) srcVal.Ptr());
+
default:
assert(!"Unhandled type");
return SLANG_FAIL;
diff --git a/tools/render-test/shader-input-layout.cpp b/tools/render-test/shader-input-layout.cpp
index 5783c6eb8..c5f1cb6dd 100644
--- a/tools/render-test/shader-input-layout.cpp
+++ b/tools/render-test/shader-input-layout.cpp
@@ -381,6 +381,142 @@ namespace renderer_test
}
}
+ RefPtr<ShaderInputLayout::Val> parseNumericValExpr(TokenReader& parser, bool negate = false)
+ {
+ switch(parser.NextToken().Type)
+ {
+ case TokenType::IntLiteral:
+ {
+ RefPtr<ShaderInputLayout::DataVal> val = new ShaderInputLayout::DataVal;
+
+ uint32_t value = parser.ReadUInt();
+ if(negate) value = uint32_t(-int32_t(value));
+ val->bufferData.add(value);
+
+ return val;
+ }
+ break;
+
+ case TokenType::DoubleLiteral:
+ {
+ RefPtr<ShaderInputLayout::DataVal> val = new ShaderInputLayout::DataVal;
+
+ float floatValue = parser.ReadFloat();
+ if(negate) floatValue = -floatValue;
+
+ uint32_t value = 0;
+ memcpy(&value, &floatValue, sizeof(floatValue));
+ val->bufferData.add(value);
+
+ return val;
+ }
+ break;
+
+ default:
+ throw ShaderInputLayoutFormatException(String("Expected a numeric literal but found '") + parser.NextToken().Content + String("' at line") + String(parser.NextToken().Position.Line));
+ }
+ }
+
+ String parseTypeName(TokenReader& parser)
+ {
+ return parser.ReadWord();
+ }
+
+ RefPtr<ShaderInputLayout::Val> parseValExpr(TokenReader& parser)
+ {
+ switch(parser.NextToken().Type)
+ {
+ case TokenType::OpSub:
+ {
+ parser.ReadToken();
+ return parseNumericValExpr(parser, true);
+ }
+ break;
+
+ case TokenType::IntLiteral:
+ case TokenType::DoubleLiteral:
+ return parseNumericValExpr(parser);
+
+ case TokenType::LBrace:
+ {
+ // aggregate
+ parser.ReadToken();
+ RefPtr<ShaderInputLayout::AggVal> val = new ShaderInputLayout::AggVal;
+
+ while( !parser.IsEnd() && !parser.LookAhead(TokenType::RBrace) )
+ {
+ ShaderInputLayout::Field field;
+
+ if( parser.LookAhead(TokenType::Identifier) && parser.NextToken(1).Type == TokenType::Colon )
+ {
+ field.name = parser.ReadWord();
+ parser.Read(TokenType::Colon);
+ }
+
+ field.val = parseValExpr(parser);
+
+ val->fields.add(field);
+
+ if(parser.LookAhead(TokenType::RBrace))
+ break;
+
+ parser.Read(TokenType::Comma);
+ }
+ parser.Read(TokenType::RBrace);
+
+
+ return val;
+ }
+ break;
+
+ case TokenType::LBracket:
+ {
+ // array
+ parser.ReadToken();
+ RefPtr<ShaderInputLayout::ArrayVal> val = new ShaderInputLayout::ArrayVal;
+
+ while( !parser.IsEnd() && !parser.LookAhead(TokenType::RBracket) )
+ {
+ val->vals.add(parseValExpr(parser));
+
+ if(parser.LookAhead(TokenType::RBracket))
+ break;
+
+ parser.Read(TokenType::Comma);
+ }
+ parser.Read(TokenType::RBracket);
+
+ return val;
+ }
+ break;
+
+ case TokenType::Identifier:
+ {
+ if( parser.AdvanceIf("new") )
+ {
+ RefPtr<ShaderInputLayout::ObjectVal> val = new ShaderInputLayout::ObjectVal;
+
+ if( parser.NextToken().Type == TokenType::Identifier )
+ {
+ val->typeName = parseTypeName(parser);
+ }
+
+ val->contentVal = parseValExpr(parser);
+ return val;
+ }
+ else
+ {
+ // TODO: other named cases
+ throw ShaderInputLayoutFormatException(String("Unexpected '") + parser.NextToken().Content + String("' at line") + String(parser.NextToken().Position.Line));
+ }
+ }
+ break;
+
+ default:
+ throw ShaderInputLayoutFormatException(String("Unexpected '") + parser.NextToken().Content + String("' at line") + String(parser.NextToken().Position.Line));
+ }
+ }
+
RefPtr<ShaderInputLayout::Val> parseVal(TokenReader& parser)
{
auto word = parser.NextToken().Content;
@@ -507,6 +643,44 @@ namespace renderer_test
parser.ReadToken();
}
+ String parseName(TokenReader& parser)
+ {
+ StringBuilder builder;
+
+ Token nameToken = parser.ReadToken();
+ if (nameToken.Type != TokenType::Identifier)
+ {
+ throw ShaderInputLayoutFormatException(StringBuilder() << "Invalid input syntax at line " << parser.NextToken().Position.Line);
+ }
+ builder << nameToken.Content;
+
+ for(;;)
+ {
+ Token token = parser.NextToken(0);
+
+ if (token.Type == TokenType::LBracket)
+ {
+ parser.ReadToken();
+ int index = parser.ReadInt();
+ SLANG_ASSERT(index >= 0);
+ parser.ReadMatchingToken(TokenType::RBracket);
+
+ builder << "[" << index << "]";
+ }
+ else if (token.Type == TokenType::Dot)
+ {
+ parser.ReadToken();
+ Token identifierToken = parser.ReadMatchingToken(TokenType::Identifier);
+
+ builder << "." << identifierToken.Content;
+ }
+ else
+ {
+ return builder;
+ }
+ }
+ }
+
void parseFieldBindings(TokenReader& parser, ShaderInputLayout::Field& ioField)
{
// parse bindings
@@ -527,47 +701,7 @@ namespace renderer_test
parser.ReadToken();
}
- StringBuilder builder;
-
- Token nameToken = parser.ReadToken();
- if (nameToken.Type != TokenType::Identifier)
- {
- throw ShaderInputLayoutFormatException(StringBuilder() << "Invalid input syntax at line " << parser.NextToken().Position.Line);
- }
- builder << nameToken.Content;
-
- while (!parser.IsEnd())
- {
- Token token = parser.NextToken(0);
-
- if (token.Type == TokenType::LBracket)
- {
- parser.ReadToken();
- int index = parser.ReadInt();
- SLANG_ASSERT(index >= 0);
- parser.ReadMatchingToken(TokenType::RBracket);
-
- builder << "[" << index << "]";
- }
- else if (token.Type == TokenType::Dot)
- {
- parser.ReadToken();
- Token identifierToken = parser.ReadMatchingToken(TokenType::Identifier);
-
- builder << "." << identifierToken.Content;
- }
- else if (token.Type == TokenType::Comma)
- {
- // Break out
- break;
- }
- else
- {
- throw ShaderInputLayoutFormatException(StringBuilder() << "Invalid input syntax at line " << parser.NextToken().Position.Line);
- }
- }
-
- ioField.name = builder;
+ ioField.name = parseName(parser);
}
else
{
@@ -598,6 +732,18 @@ namespace renderer_test
parentForNewVal->addField(field);
}
+ void parseSetEntry(TokenReader& parser)
+ {
+ auto parentForNewVal = parentVal;
+
+ ShaderInputLayout::Field field;
+ field.name = parseName(parser);
+ parser.Read(TokenType::OpAssign);
+ field.val = parseValExpr(parser);
+
+ parentForNewVal->addField(field);
+ }
+
void parseLine(TokenReader& parser)
{
if (parser.LookAhead("entryPointSpecializationArg")
@@ -629,6 +775,10 @@ namespace renderer_test
parentVal = parentValStack.getLast();
parentValStack.removeLast();
}
+ else if( parser.AdvanceIf("set") )
+ {
+ parseSetEntry(parser);
+ }
else
{
parseValEntry(parser);