aboutsummaryrefslogtreecommitdiff
path: root/src/amberscript/parser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/amberscript/parser.cc')
-rw-r--r--src/amberscript/parser.cc637
1 files changed, 635 insertions, 2 deletions
diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc
index 6eb3431..14bd386 100644
--- a/src/amberscript/parser.cc
+++ b/src/amberscript/parser.cc
@@ -1,4 +1,5 @@
// Copyright 2018 The Amber Authors.
+// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,6 +23,7 @@
#include <utility>
#include <vector>
+#include "amber/vulkan_header.h"
#include "src/image.h"
#include "src/make_unique.h"
#include "src/sampler.h"
@@ -324,6 +326,8 @@ Result Parser::Parse(const std::string& data) {
r = ParseSampler();
} else if (tok == "VIRTUAL_FILE") {
r = ParseVirtualFile();
+ } else if (tok == "ACCELERATION_STRUCTURE") {
+ r = ParseAS();
} else {
r = Result("unknown token: " + tok);
}
@@ -406,6 +410,18 @@ Result Parser::ToShaderType(const std::string& str, ShaderType* type) {
*type = kShaderTypeTessellationControl;
else if (str == "compute")
*type = kShaderTypeCompute;
+ else if (str == "ray_generation")
+ *type = kShaderTypeRayGeneration;
+ else if (str == "any_hit")
+ *type = kShaderTypeAnyHit;
+ else if (str == "closest_hit")
+ *type = kShaderTypeClosestHit;
+ else if (str == "miss")
+ *type = kShaderTypeMiss;
+ else if (str == "intersection")
+ *type = kShaderTypeIntersection;
+ else if (str == "callable")
+ *type = kShaderTypeCall;
else if (str == "multi")
*type = kShaderTypeMulti;
else
@@ -438,6 +454,8 @@ Result Parser::ToPipelineType(const std::string& str, PipelineType* type) {
*type = PipelineType::kCompute;
else if (str == "graphics")
*type = PipelineType::kGraphics;
+ else if (str == "raytracing")
+ *type = PipelineType::kRayTracing;
else
return Result("unknown pipeline type: " + str);
return {};
@@ -550,6 +568,9 @@ Result Parser::ParseShaderBlock() {
if (!token->IsIdentifier() || token->AsString() != "END")
return Result("SHADER missing END command");
+ if (shader->GetTargetEnv().empty() && IsRayTracingShader(type))
+ shader->SetTargetEnv("spv1.4");
+
r = script_->AddShader(std::move(shader));
if (!r.IsSuccess())
return r;
@@ -626,6 +647,10 @@ Result Parser::ParsePipelineBody(const std::string& cmd_name,
r = ParsePipelinePatchControlPoints(pipeline.get());
} else if (tok == "BLEND") {
r = ParsePipelineBlend(pipeline.get());
+ } else if (tok == "SHADER_GROUP") {
+ r = ParsePipelineShaderGroup(pipeline.get());
+ } else if (tok == "SHADER_BINDING_TABLE") {
+ r = ParseSBT(pipeline.get());
} else {
r = Result("unknown token in pipeline block: " + tok);
}
@@ -1077,8 +1102,8 @@ Result Parser::ParsePipelineBind(Pipeline* pipeline) {
if (!token->IsIdentifier()) {
return Result(
- "missing BUFFER, BUFFER_ARRAY, SAMPLER, or SAMPLER_ARRAY in BIND "
- "command");
+ "missing BUFFER, BUFFER_ARRAY, SAMPLER, SAMPLER_ARRAY, or "
+ "ACCELERATION_STRUCTURE in BIND command");
}
auto object_type = token->AsString();
@@ -1418,6 +1443,38 @@ Result Parser::ParsePipelineBind(Pipeline* pipeline) {
} else {
return Result("missing DESCRIPTOR_SET or KERNEL for BIND command");
}
+ } else if (object_type == "ACCELERATION_STRUCTURE") {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result(
+ "missing top level acceleration structure name in BIND command");
+
+ TLAS* tlas = script_->GetTLAS(token->AsString());
+ if (!tlas)
+ return Result("unknown top level acceleration structure: " +
+ token->AsString());
+
+ token = tokenizer_->NextToken();
+ if (token->AsString() == "DESCRIPTOR_SET") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("invalid value for DESCRIPTOR_SET in BIND command");
+ uint32_t descriptor_set = token->AsUint32();
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "BINDING")
+ return Result("missing BINDING for BIND command");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("invalid value for BINDING in BIND command");
+
+ uint32_t binding = token->AsUint32();
+
+ pipeline->AddTLAS(tlas, descriptor_set, binding);
+ } else {
+ return Result("missing DESCRIPTOR_SET or BINDING in BIND command");
+ }
} else {
return Result("missing BUFFER or SAMPLER in BIND command");
}
@@ -1957,6 +2014,81 @@ Result Parser::ParsePipelineBlend(Pipeline* pipeline) {
return ValidateEndOfStatement("BLEND command");
}
+Result Parser::ParsePipelineShaderGroup(Pipeline* pipeline) {
+ std::unique_ptr<Token> token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("Group name expected");
+
+ auto tok = token->AsString();
+ if (pipeline->GetShaderGroup(tok))
+ return Result("Group name already exists");
+ std::unique_ptr<ShaderGroup> group = MakeUnique<ShaderGroup>();
+ group->SetName(tok);
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL() || token->IsEOS())
+ break;
+ if (!token->IsIdentifier())
+ return Result("Shader name expected");
+
+ tok = token->AsString();
+ Shader* shader = script_->GetShader(tok);
+ if (shader == nullptr)
+ return Result("Shader not found: " + tok);
+
+ Result r = pipeline->AddShader(shader, shader->GetType());
+ if (!r.IsSuccess())
+ return r;
+
+ switch (shader->GetType()) {
+ case kShaderTypeRayGeneration:
+ case kShaderTypeMiss:
+ case kShaderTypeCall: {
+ if (group->IsHitGroup())
+ return Result("Hit group cannot contain general shaders");
+ if (group->GetGeneralShader() != nullptr)
+ return Result("Two general shaders cannot be in one group");
+ group->SetGeneralShader(shader);
+ break;
+ }
+ case kShaderTypeAnyHit: {
+ if (group->IsGeneralGroup())
+ return Result("General group cannot contain any hit shaders");
+ if (group->GetAnyHitShader() != nullptr)
+ return Result("Two any hit shaders cannot be in one group");
+ group->SetAnyHitShader(shader);
+ break;
+ }
+ case kShaderTypeClosestHit: {
+ if (group->IsGeneralGroup())
+ return Result("General group cannot contain closest hit shaders");
+ if (group->GetClosestHitShader() != nullptr)
+ return Result("Two closest hit shaders cannot be in one group");
+ group->SetClosestHitShader(shader);
+ break;
+ }
+ case kShaderTypeIntersection: {
+ if (group->IsGeneralGroup())
+ return Result("General group cannot contain intersection shaders");
+ if (group->GetIntersectionShader() != nullptr)
+ return Result("Two intersection shaders cannot be in one group");
+ group->SetIntersectionShader(shader);
+ break;
+ }
+ default:
+ return Result("Shader must be of raytracing type");
+ }
+ }
+
+ if (!group->IsGeneralGroup() && !group->IsHitGroup())
+ return Result("No shaders in shader group defined");
+
+ pipeline->AddShaderGroup(std::move(group));
+
+ return {};
+}
+
Result Parser::ParseStruct() {
auto token = tokenizer_->NextToken();
if (!token->IsIdentifier())
@@ -2574,6 +2706,71 @@ Result Parser::ParseRun() {
if (!pipeline)
return Result("unknown pipeline for RUN command: " + token->AsString());
+ if (pipeline->IsRayTracing()) {
+ auto cmd = MakeUnique<RayTracingCommand>(pipeline);
+ cmd->SetLine(line);
+
+ while (true) {
+ if (tokenizer_->PeekNextToken()->IsInteger())
+ break;
+
+ token = tokenizer_->NextToken();
+
+ if (token->IsEOL() || token->IsEOS())
+ return Result("Incomplete RUN command");
+
+ if (!token->IsIdentifier())
+ return Result("Shader binding table type is expected");
+
+ std::string tok = token->AsString();
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("Shader binding table name expected");
+
+ std::string sbtname = token->AsString();
+ if (pipeline->GetSBT(sbtname) == nullptr)
+ return Result("Shader binding table with this name was not defined");
+
+ if (tok == "RAYGEN") {
+ if (!cmd->GetRayGenSBTName().empty())
+ return Result("RAYGEN shader binding table can specified only once");
+ cmd->SetRayGenSBTName(sbtname);
+ } else if (tok == "MISS") {
+ if (!cmd->GetMissSBTName().empty())
+ return Result("MISS shader binding table can specified only once");
+ cmd->SetMissSBTName(sbtname);
+ } else if (tok == "HIT") {
+ if (!cmd->GetHitsSBTName().empty())
+ return Result("HIT shader binding table can specified only once");
+ cmd->SetHitsSBTName(sbtname);
+ } else if (tok == "CALL") {
+ if (!cmd->GetCallSBTName().empty())
+ return Result("CALL shader binding table can specified only once");
+ cmd->SetCallSBTName(sbtname);
+ } else {
+ return Result("Unknown shader binding table type");
+ }
+ }
+
+ for (int i = 0; i < 3; i++) {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsInteger())
+ return Result("invalid parameter for RUN command: " +
+ token->ToOriginalString());
+ if (i == 0)
+ cmd->SetX(token->AsUint32());
+ else if (i == 1)
+ cmd->SetY(token->AsUint32());
+ else
+ cmd->SetZ(token->AsUint32());
+ }
+
+ command_list_.push_back(std::move(cmd));
+ return ValidateEndOfStatement("RUN command");
+ }
+
token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("RUN command requires parameters");
@@ -3728,6 +3925,442 @@ Result Parser::ParseSampler() {
return script_->AddSampler(std::move(sampler));
}
+bool Parser::IsRayTracingShader(ShaderType type) {
+ return type == kShaderTypeRayGeneration || type == kShaderTypeAnyHit ||
+ type == kShaderTypeClosestHit || type == kShaderTypeMiss ||
+ type == kShaderTypeIntersection || type == kShaderTypeCall;
+}
+
+Result Parser::ParseAS() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("Acceleration structure requires TOP_LEVEL or BOTTOM_LEVEL");
+
+ Result r;
+ auto type = token->AsString();
+ if (type == "BOTTOM_LEVEL")
+ r = ParseBLAS();
+ else if (type == "TOP_LEVEL")
+ r = ParseTLAS();
+ else
+ return Result("Unexpected acceleration structure type");
+
+ return r;
+}
+
+Result Parser::ParseBLAS() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("Bottom level acceleration structure requires a name");
+
+ auto name = token->AsString();
+ if (script_->GetBLAS(name) != nullptr)
+ return Result(
+ "Bottom level acceleration structure with this name already defined");
+
+ std::unique_ptr<BLAS> blas = MakeUnique<BLAS>();
+ blas->SetName(name);
+
+ token = tokenizer_->NextToken();
+ if (!token->IsEOL())
+ return Result("New line expected");
+
+ Result r;
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL()) {
+ continue;
+ }
+ if (token->IsEOS()) {
+ return Result("END command missing");
+ }
+ if (!token->IsIdentifier()) {
+ return Result("Identifier expected");
+ }
+
+ auto geom = token->AsString();
+ if (geom == "END") {
+ break;
+ } else if (geom == "GEOMETRY") {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier()) {
+ return Result("Identifier expected");
+ }
+
+ auto type = token->AsString();
+ if (type == "TRIANGLES") {
+ r = ParseBLASTriangle(blas.get());
+ } else if (type == "AABBS") {
+ r = ParseBLASAABB(blas.get());
+ } else {
+ return Result("Unexpected geometry type");
+ }
+ } else {
+ return Result("Unexpected identifier");
+ }
+
+ if (!r.IsSuccess()) {
+ return r;
+ }
+ }
+
+ if (blas->GetGeometrySize() > 0) {
+ auto type = blas->GetGeometries()[0]->GetType();
+ auto& geometries = blas->GetGeometries();
+ for (auto& g : geometries)
+ if (g->GetType() != type)
+ return Result("Only one type of geometry is allowed within a BLAS");
+ }
+
+ return script_->AddBLAS(std::move(blas));
+}
+
+Result Parser::ParseBLASTriangle(BLAS* blas) {
+ std::unique_ptr<Geometry> geometry = MakeUnique<Geometry>();
+ std::vector<float> g;
+ geometry->SetType(GeometryType::kTriangle);
+
+ while (true) {
+ auto token = tokenizer_->NextToken();
+
+ if (token->IsEOS())
+ return Result("END expected");
+ if (token->IsEOL())
+ continue;
+
+ if (token->IsIdentifier()) {
+ std::string tok = token->AsString();
+ if (tok == "END") {
+ break;
+ } else {
+ return Result("END or float value is expected");
+ }
+ } else if (token->IsInteger() || token->IsDouble()) {
+ g.push_back(token->AsFloat());
+ } else {
+ return Result("Unexpected data type");
+ }
+ }
+
+ if (g.empty())
+ return Result("No triangles have been specified.");
+
+ if (g.size() % 3 != 0)
+ return Result("Each vertex consists of three float coordinates.");
+
+ if ((g.size() / 3) % 3 != 0)
+ return Result("Each triangle should include three vertices.");
+
+ geometry->SetData(g);
+
+ blas->AddGeometry(&geometry);
+
+ return {};
+}
+
+Result Parser::ParseBLASAABB(BLAS* blas) {
+ std::unique_ptr<Geometry> geometry = MakeUnique<Geometry>();
+ std::vector<float> g;
+ geometry->SetType(GeometryType::kAABB);
+
+ while (true) {
+ auto token = tokenizer_->NextToken();
+
+ if (token->IsEOS())
+ return Result("END expected");
+ if (token->IsEOL())
+ continue;
+
+ if (token->IsIdentifier()) {
+ std::string tok = token->AsString();
+ if (tok == "END") {
+ break;
+ } else {
+ return Result("END or float value is expected");
+ }
+ } else if (token->IsInteger() || token->IsDouble()) {
+ g.push_back(token->AsFloat());
+ } else {
+ return Result("Unexpected data type");
+ }
+ }
+
+ if (g.empty())
+ return Result("No AABBs have been specified.");
+
+ if ((g.size() % 6) != 0)
+ return Result(
+ "Each vertex consists of three float coordinates. Each AABB should "
+ "include two vertices.");
+
+ geometry->SetData(g);
+
+ blas->AddGeometry(&geometry);
+
+ return {};
+}
+
+Result Parser::ParseTLAS() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("invalid TLAS name provided");
+
+ auto name = token->AsString();
+
+ token = tokenizer_->NextToken();
+ if (!token->IsEOL())
+ return Result("New line expected");
+
+ std::unique_ptr<TLAS> tlas = MakeUnique<TLAS>();
+
+ tlas->SetName(name);
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL())
+ continue;
+ if (token->IsEOS())
+ return Result("END command missing");
+ if (!token->IsIdentifier())
+ return Result("expected identifier");
+
+ Result r;
+ std::string tok = token->AsString();
+ if (tok == "END")
+ break;
+ if (tok == "BOTTOM_LEVEL_INSTANCE")
+ r = ParseBLASInstance(tlas.get());
+ else
+ r = Result("unknown token: " + tok);
+
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ Result r = script_->AddTLAS(std::move(tlas));
+ if (!r.IsSuccess())
+ return r;
+
+ return {};
+}
+
+// BOTTOM_LEVEL_INSTANCE <blas_name> [MASK 0-255] [OFFSET 0-16777215] [INDEX
+// 0-16777215] [FLAGS {flags}] [TRANSFORM {float x 12} END]
+Result Parser::ParseBLASInstance(TLAS* tlas) {
+ std::unique_ptr<Token> token;
+ std::unique_ptr<BLASInstance> instance = MakeUnique<BLASInstance>();
+
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("Bottom level acceleration structure name expected");
+
+ std::string name = token->AsString();
+ auto ptr = script_->GetBLAS(name);
+
+ if (!ptr)
+ return Result(
+ "Bottom level acceleration structure with given name not found");
+
+ instance->SetUsedBLAS(name, ptr);
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOS())
+ return Result("Unexpected end");
+ if (token->IsEOL())
+ continue;
+
+ if (!token->IsIdentifier())
+ return Result("expected identifier");
+
+ Result r;
+ std::string tok = token->AsString();
+ if (tok == "END") {
+ break;
+ } else if (tok == "TRANSFORM") {
+ r = ParseBLASInstanceTransform(instance.get());
+ } else if (tok == "FLAGS") {
+ r = ParseBLASInstanceFlags(instance.get());
+ } else if (tok == "MASK") {
+ token = tokenizer_->NextToken();
+ uint64_t v;
+
+ if (token->IsInteger())
+ v = token->AsUint64();
+ else if (token->IsHex())
+ v = token->AsHex();
+ else
+ return Result("Integer or hex value expected");
+
+ instance->SetMask(uint32_t(v));
+ } else if (tok == "OFFSET") {
+ token = tokenizer_->NextToken();
+ uint64_t v;
+
+ if (token->IsInteger())
+ v = token->AsUint64();
+ else if (token->IsHex())
+ v = token->AsHex();
+ else
+ return Result("Integer or hex value expected");
+
+ instance->SetOffset(uint32_t(v));
+ } else if (tok == "INDEX") {
+ token = tokenizer_->NextToken();
+ uint64_t v;
+
+ if (token->IsInteger())
+ v = token->AsUint64();
+ else if (token->IsHex())
+ v = token->AsHex();
+ else
+ return Result("Integer or hex value expected");
+
+ instance->SetInstanceIndex(uint32_t(v));
+ } else {
+ r = Result("Unknown token in BOTTOM_LEVEL_INSTANCE block: " + tok);
+ }
+
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ tlas->AddInstance(std::move(instance));
+
+ return {};
+}
+
+Result Parser::ParseBLASInstanceTransform(BLASInstance* instance) {
+ std::unique_ptr<Token> token;
+ std::vector<float> transform;
+
+ transform.reserve(12);
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL())
+ continue;
+ if (token->IsEOS())
+ return Result("END command missing");
+
+ if (token->IsIdentifier() && token->AsString() == "END")
+ break;
+ else if (token->IsDouble() || token->IsInteger())
+ transform.push_back(token->AsFloat());
+ else
+ return Result("Unknown token: " + token->AsString());
+ }
+
+ if (transform.size() != 12)
+ return Result("Transform matrix expected to have 12 numbers");
+
+ instance->SetTransform(transform);
+
+ return {};
+}
+
+Result Parser::ParseBLASInstanceFlags(BLASInstance* instance) {
+ std::unique_ptr<Token> token;
+ uint32_t flags = 0;
+ bool first_eol = true;
+ bool singleline = true;
+ Result r;
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL()) {
+ if (first_eol) {
+ first_eol = false;
+ singleline = (flags != 0);
+ }
+ if (singleline)
+ break;
+ else
+ continue;
+ }
+ if (token->IsEOS())
+ return Result("END command missing");
+
+ if (token->IsInteger()) {
+ flags |= token->AsUint32();
+ } else if (token->IsHex()) {
+ flags |= uint32_t(token->AsHex());
+ } else if (token->IsIdentifier()) {
+ if (token->AsString() == "END")
+ break;
+ else if (token->AsString() == "TRIANGLE_FACING_CULL_DISABLE")
+ flags |= VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
+ else if (token->AsString() == "TRIANGLE_FLIP_FACING")
+ flags |= VK_GEOMETRY_INSTANCE_TRIANGLE_FLIP_FACING_BIT_KHR;
+ else if (token->AsString() == "FORCE_OPAQUE")
+ flags |= VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR;
+ else if (token->AsString() == "FORCE_NO_OPAQUE")
+ flags |= VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_KHR;
+ else if (token->AsString() == "FORCE_OPACITY_MICROMAP_2_STATE")
+ flags |= VK_GEOMETRY_INSTANCE_FORCE_OPACITY_MICROMAP_2_STATE_EXT;
+ else if (token->AsString() == "DISABLE_OPACITY_MICROMAPS")
+ flags |= VK_GEOMETRY_INSTANCE_DISABLE_OPACITY_MICROMAPS_EXT;
+ else
+ return Result("Unknown flag: " + token->AsString());
+ } else {
+ r = Result("Identifier expected");
+ }
+
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ if (r.IsSuccess())
+ instance->SetFlags(flags);
+
+ return {};
+}
+
+Result Parser::ParseSBT(Pipeline* pipeline) {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("SHADER_BINDINGS_TABLE requires a name");
+
+ auto name = token->AsString();
+ if (pipeline->GetSBT(name) != nullptr)
+ return Result("SHADER_BINDINGS_TABLE with this name already defined");
+
+ std::unique_ptr<SBT> sbt = MakeUnique<SBT>();
+ sbt->SetName(name);
+
+ token = tokenizer_->NextToken();
+ if (!token->IsEOL())
+ return Result("New line expected");
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL()) {
+ continue;
+ }
+ if (token->IsEOS()) {
+ return Result("END command missing");
+ }
+ if (!token->IsIdentifier()) {
+ return Result("Identifier expected");
+ }
+
+ auto tok = token->AsString();
+ if (tok == "END") {
+ break;
+ }
+
+ std::unique_ptr<SBTRecord> sbtrecord = MakeUnique<SBTRecord>();
+
+ sbtrecord->SetUsedShaderGroupName(tok, pipeline->GetShaderGroupIndex(tok));
+ sbtrecord->SetCount(1);
+
+ sbt->AddSBTRecord(std::move(sbtrecord));
+ }
+
+ return pipeline->AddSBT(std::move(sbt));
+}
+
Result Parser::ParseTolerances(std::vector<Probe::Tolerance>* tolerances) {
auto token = tokenizer_->PeekNextToken();
while (!token->IsEOL() && !token->IsEOS()) {