Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ LOCAL_SRC_FILES:= \
src/type_parser.cc \
src/value.cc \
src/verifier.cc \
src/virtual_file_store.cc \
src/vkscript/command_parser.cc \
src/vkscript/datum_type_parser.cc \
src/vkscript/parser.cc \
Expand Down
72 changes: 57 additions & 15 deletions docs/amber_script.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,63 @@ set of data types.
SET ENGINE_DATA {engine data variable} {value}*
```

### Virtual File Store

Each amber script contains a virtual file system that can store files of textual
data. This lets you bundle multiple source files into a single, hermetic amber
script file.

Virtual files are declared using the `VIRTUAL_FILE` command:

```groovy
VIRTUAL_FILE {path}
{file-content}
END
```

Paths must be unique.

Shaders can directly reference these virtual files for their source. \
HLSL shaders that `#include` other `.hlsl` files will first check the virtual
file system, before falling back to the standard file system.

### Shaders

Shader programs are declared using the `SHADER` command. \
Shaders can be declared as `PASSTHROUGH`, with inlined source or using source
from a `VIRTUAL_FILE`.

Pass-through shader:

```groovy
# Creates a passthrough vertex shader. The shader passes the vec4 at input
# location 0 through to the `gl_Position`.
SHADER vertex {shader_name} PASSTHROUGH
```

Shader using inlined source:

```groovy
# Creates a shader of |shader_type| with the given |shader_name|. The shader
# will be of |shader_format|. The shader source then follows and is terminated
# with the |END| tag.
SHADER {shader_type} {shader_name} {shader_format}
{shader_source}
END
```

Shader using source from `VIRTUAL_FILE`:

```groovy
# Creates a shader of |shader_type| with the given |shader_name|. The shader
# will be of |shader_format|. The shader will use the virtual file with |path|.
SHADER {shader_type} {shader_name} {shader_format} VIRTUAL_FILE {path}
```

`{shader_name}` is used to identify the shader to attach to `PIPELINE`s,

`{shader_type}` and `{shader_format}` are described below:

#### Shader Type
* `vertex`
* `fragment`
Expand All @@ -92,24 +147,11 @@ types, but in that case must only provide a single shader type in the module.

#### Shader Format
* `GLSL`  (with glslang)
* `HLSL`  (with dxc or glslang if dxc disabled) -- future
* `HLSL`  (with dxc or glslang if dxc disabled)
* `SPIRV-ASM` (with spirv-as)
* `SPIRV-HEX` (decoded straight to SPIR-V)
* `OPENCL-C` (with clspv)

```groovy
# Creates a passthrough vertex shader. The shader passes the vec4 at input
# location 0 through to the `gl_Position`.
SHADER vertex {shader_name} PASSTHROUGH

# Creates a shader of |shader_type| with the given |shader_name|. The shader
# will be of |shader_format|. The shader should then be inlined before the
# |END| tag.
SHADER {shader_type} {shader_name} {shader_format}
...
END
```

### Buffers

An AmberScript buffer represents a set of contiguous bits. This can be used for
Expand Down Expand Up @@ -471,7 +513,7 @@ RUN {pipeline_name} \

```groovy
# Run the given |pipeline_name| which must be a `graphics` pipeline. The
# grid at |x|, |y|, |width|x|height|, |columns|x|rows| will be rendered.
# grid at |x|, |y|, |width|x|height|, |columns|x|rows| will be rendered.
# Ignores VERTEX_DATA and INDEX_DATA on the given pipeline.
# For columns, rows of (5, 4) a total of 5*4=20 rectangles will be drawn.
RUN {pipeline_name} \
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ set(AMBER_SOURCES
type_parser.cc
value.cc
verifier.cc
virtual_file_store.cc
vkscript/command_parser.cc
vkscript/datum_type_parser.cc
vkscript/parser.cc
Expand Down Expand Up @@ -163,6 +164,7 @@ if (${AMBER_ENABLE_TESTS})
type_parser_test.cc
type_test.cc
verifier_test.cc
virtual_file_store_test.cc
vkscript/command_parser_test.cc
vkscript/datum_type_parser_test.cc
vkscript/parser_test.cc
Expand Down
65 changes: 55 additions & 10 deletions src/amberscript/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ Result Parser::Parse(const std::string& data) {
r = ParseStruct();
} else if (tok == "SAMPLER") {
r = ParseSampler();
} else if (tok == "VIRTUAL_FILE") {
r = ParseVirtualFile();
} else {
r = Result("unknown token: " + tok);
}
Expand Down Expand Up @@ -370,19 +372,42 @@ Result Parser::ParseShaderBlock() {

shader->SetFormat(format);

r = ValidateEndOfStatement("SHADER command");
if (!r.IsSuccess())
return r;
token = tokenizer_->PeekNextToken();
if (token->IsIdentifier() && token->AsString() == "VIRTUAL_FILE") {
tokenizer_->NextToken(); // Skip VIRTUAL_FILE

token = tokenizer_->NextToken();
if (!token->IsIdentifier() && !token->IsString())
return Result("expected virtual file path after VIRTUAL_FILE");

std::string data = tokenizer_->ExtractToNext("END");
if (data.empty())
return Result("SHADER must not be empty");
r = ValidateEndOfStatement("SHADER command");
if (!r.IsSuccess())
return r;

shader->SetData(data);
auto path = token->AsString();

token = tokenizer_->NextToken();
if (!token->IsIdentifier() || token->AsString() != "END")
return Result("SHADER missing END command");
std::string data;
r = script_->GetVirtualFile(path, &data);
if (!r.IsSuccess()) {
return r;
}

shader->SetData(data);
} else {
r = ValidateEndOfStatement("SHADER command");
if (!r.IsSuccess())
return r;

std::string data = tokenizer_->ExtractToNext("END");
if (data.empty())
return Result("SHADER must not be empty");

shader->SetData(data);

token = tokenizer_->NextToken();
if (!token->IsIdentifier() || token->AsString() != "END")
return Result("SHADER missing END command");
}

r = script_->AddShader(std::move(shader));
if (!r.IsSuccess())
Expand Down Expand Up @@ -2896,5 +2921,25 @@ Result Parser::ParseTolerances(std::vector<Probe::Tolerance>* tolerances) {
return {};
}

Result Parser::ParseVirtualFile() {
auto token = tokenizer_->NextToken();
if (!token->IsIdentifier() && !token->IsString())
return Result("invalid virtual file path");

auto path = token->AsString();

auto r = ValidateEndOfStatement("VIRTUAL_FILE command");
if (!r.IsSuccess())
return r;

auto data = tokenizer_->ExtractToNext("END");

token = tokenizer_->NextToken();
if (!token->IsIdentifier() || token->AsString() != "END")
return Result("VIRTUAL_FILE missing END command");

return script_->AddVirtualFile(path, data);
}

} // namespace amberscript
} // namespace amber
2 changes: 2 additions & 0 deletions src/amberscript/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ class Parser : public amber::Parser {
Format* fmt,
std::vector<Value>* values);

Result ParseVirtualFile();

std::unique_ptr<Tokenizer> tokenizer_;
std::vector<std::unique_ptr<Command>> command_list_;
};
Expand Down
49 changes: 49 additions & 0 deletions src/amberscript/parser_shader_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,54 @@ END)";
EXPECT_EQ("2: extra parameters after SHADER command: INVALID", r.Error());
}

TEST_F(AmberScriptParserTest, ShaderVirtualFile) {
std::string in = R"(#!amber
VIRTUAL_FILE my_shader.hlsl
My shader source
END

SHADER vertex my_shader HLSL VIRTUAL_FILE my_shader.hlsl
)";

Parser parser;
Result r = parser.Parse(in);
ASSERT_EQ(r.Error(), "");

auto script = parser.GetScript();
auto shader = script->GetShader("my_shader");
ASSERT_TRUE(shader != nullptr);
auto source = shader->GetData();
ASSERT_EQ("My shader source\n", shader->GetData());
}

TEST_F(AmberScriptParserTest, VirtualFileDuplicatePath) {
std::string in = R"(#!amber
VIRTUAL_FILE my.file
Blah
END

VIRTUAL_FILE my.file
Blah
END
)";

Parser parser;
Result r = parser.Parse(in);
ASSERT_EQ(r.Error(), "8: Virtual file 'my.file' already declared");
}

TEST_F(AmberScriptParserTest, VirtualFileEmptyPath) {
std::string in = R"(#!amber
VIRTUAL_FILE ""
Blah
END
)";

Parser parser;
Result r = parser.Parse(in);
ASSERT_EQ(r.Error(), "4: Virtual file path was empty");
}

struct ShaderTypeData {
const char* name;
ShaderType type;
Expand Down Expand Up @@ -315,6 +363,7 @@ TEST_P(AmberScriptParserShaderFormatTest, ShaderFormats) {
EXPECT_EQ(test_data.format, shader->GetFormat());
EXPECT_EQ(shader_result, shader->GetData());
}

INSTANTIATE_TEST_SUITE_P(
AmberScriptParserTestsShaderFormat,
AmberScriptParserShaderFormatTest,
Expand Down
Loading