diff options
author | alan-baker <alanbaker@google.com> | 2019-07-23 14:50:34 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-23 14:50:34 -0400 |
commit | 7bc793fa18d8f8a518492ed75ed414683c704236 (patch) | |
tree | 63996f212526d1cdc3372e85d636587a58fed5f5 | |
parent | 1c7b905284129c4a9f2f735e08da6f53b4eb4d9b (diff) | |
download | amber-7bc793fa18d8f8a518492ed75ed414683c704236.tar.gz |
Add support for specifying compile options (#592)
Fixes #433
* Adds COMPILE_OPTIONS command
* currently only supported for OPENCL-C shaders
* plumbed to Clspv
* Added tests
* Add docs
-rw-r--r-- | docs/amber_script.md | 38 | ||||
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/amberscript/parser.cc | 39 | ||||
-rw-r--r-- | src/amberscript/parser.h | 1 | ||||
-rw-r--r-- | src/amberscript/parser_compile_options_test.cc | 168 | ||||
-rw-r--r-- | src/clspv_helper.cc | 8 | ||||
-rw-r--r-- | src/pipeline.cc | 17 | ||||
-rw-r--r-- | src/pipeline.h | 11 | ||||
-rw-r--r-- | src/shader_compiler_test.cc | 52 | ||||
-rw-r--r-- | tests/cases/opencl_set_arg.amber | 53 |
10 files changed, 381 insertions, 7 deletions
diff --git a/docs/amber_script.md b/docs/amber_script.md index 93e97ba..1a3cff5 100644 --- a/docs/amber_script.md +++ b/docs/amber_script.md @@ -214,6 +214,14 @@ The following commands are all specified within the `PIPELINE` command. ``` ```groovy + # Set the compile options used to compile the given shader. Options are parsed + # the same as on the command line. Currently, only supported for OPENCL-C shaders. + COMPILE_OPTIONS {shader_name} + {option}+ + END +``` + +```groovy # Set the size of the render buffers. |width| and |height| are integers and # default to 250x250. FRAMEBUFFER_SIZE _width_ _height_ @@ -654,6 +662,36 @@ CLEAR kGraphicsPipeline RUN kGraphicsPipeline DRAW_ARRAY AS triangle_list START_IDX 0 COUNT 24 ``` +### OpenCL-C Shaders +```groovy +SHADER compute my_shader OPENCL-C +kernel void line(const int* in, global int* out, int m, int b) { + *out = *in * m + b; +} +END + +BUFFER in_buf DATA_TYPE int32 DATA 4 END +BUFFER out_buf DATA_TYPE int32 DATA 0 END + +PIPELINE compute my_pipeline + ATTACH my_shader ENTRY_POINT line + COMPILE_OPTIONS + -cluster-pod-kernel-args + -pod-ubo + -constant-args-ubo + -max-ubo-size=128 + END + BIND BUFFER in_buf KERNEL ARG_NAME in + BIND BUFFER out_buf KERNEL ARG_NAME out + SET KERNEL ARG_NAME m AS int32 3 + SET KERNEL ARG_NAME b AS int32 1 +END + +RUN my_pipeline 1 1 1 + +EXPECT out_buf EQ IDX 0 EQ 13 +``` + ### Image Formats * `A1R5G5B5_UNORM_PACK16` * `A2B10G10R10_SINT_PACK32` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cdf5c47..37e3528 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -117,6 +117,7 @@ if (${AMBER_ENABLE_TESTS}) amberscript/parser_buffer_test.cc amberscript/parser_clear_color_test.cc amberscript/parser_clear_test.cc + amberscript/parser_compile_options_test.cc amberscript/parser_copy_test.cc amberscript/parser_device_feature_test.cc amberscript/parser_expect_test.cc diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc index 82b8987..de43731 100644 --- a/src/amberscript/parser.cc +++ b/src/amberscript/parser.cc @@ -417,6 +417,8 @@ Result Parser::ParsePipelineBody(const std::string& cmd_name, r = ParsePipelineIndexData(pipeline.get()); } else if (tok == "SET") { r = ParsePipelineSet(pipeline.get()); + } else if (tok == "COMPILE_OPTIONS") { + r = ParsePipelineShaderCompileOptions(pipeline.get()); } else { r = Result("unknown token in pipeline block: " + tok); } @@ -598,6 +600,43 @@ Result Parser::ParsePipelineShaderOptimizations(Pipeline* pipeline) { return ValidateEndOfStatement("SHADER_OPTIMIZATION command"); } +Result Parser::ParsePipelineShaderCompileOptions(Pipeline* pipeline) { + auto token = tokenizer_->NextToken(); + if (!token->IsString()) + return Result("missing shader name in COMPILE_OPTIONS command"); + + auto* shader = script_->GetShader(token->AsString()); + if (!shader) + return Result("unknown shader in COMPILE_OPTIONS command"); + + if (shader->GetFormat() != kShaderFormatOpenCLC) { + return Result("COMPILE_OPTIONS currently only supports OPENCL-C shaders"); + } + + token = tokenizer_->NextToken(); + if (!token->IsEOL()) + return Result("extra parameters after COMPILE_OPTIONS command"); + + std::vector<std::string> options; + while (true) { + token = tokenizer_->NextToken(); + if (token->IsEOL()) + continue; + if (token->IsEOS()) + return Result("COMPILE_OPTIONS missing END command"); + if (token->AsString() == "END") + break; + + options.push_back(token->AsString()); + } + + Result r = pipeline->SetShaderCompileOptions(shader, options); + if (!r.IsSuccess()) + return r; + + return ValidateEndOfStatement("COMPILE_OPTIONS command"); +} + Result Parser::ParsePipelineFramebufferSize(Pipeline* pipeline) { auto token = tokenizer_->NextToken(); if (token->IsEOL() || token->IsEOS()) diff --git a/src/amberscript/parser.h b/src/amberscript/parser.h index 028ae5c..a127430 100644 --- a/src/amberscript/parser.h +++ b/src/amberscript/parser.h @@ -58,6 +58,7 @@ class Parser : public amber::Parser { Result ParsePipelineBlock(); Result ParsePipelineAttach(Pipeline*); Result ParsePipelineShaderOptimizations(Pipeline*); + Result ParsePipelineShaderCompileOptions(Pipeline*); Result ParsePipelineFramebufferSize(Pipeline*); Result ParsePipelineBind(Pipeline*); Result ParsePipelineVertexData(Pipeline*); diff --git a/src/amberscript/parser_compile_options_test.cc b/src/amberscript/parser_compile_options_test.cc new file mode 100644 index 0000000..aa181dc --- /dev/null +++ b/src/amberscript/parser_compile_options_test.cc @@ -0,0 +1,168 @@ +// Copyright 2019 The Amber Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gtest/gtest.h" +#include "src/amberscript/parser.h" + +namespace amber { +namespace amberscript { + +using AmberScriptParserTest = testing::Test; + +TEST_F(AmberScriptParserTest, PipelineShaderCompileOptions) { + std::string in = R"( +SHADER compute my_shader OPENCL-C +#shader +END +PIPELINE compute my_pipeline + ATTACH my_shader + COMPILE_OPTIONS my_shader + --option1 + --option2=blah + other + --option3 3 + END +END +)"; + + Parser parser; + Result r = parser.Parse(in); + ASSERT_TRUE(r.IsSuccess()); + + auto script = parser.GetScript(); + const auto& pipelines = script->GetPipelines(); + ASSERT_EQ(1U, pipelines.size()); + + const auto* pipeline = pipelines[0].get(); + const auto& shaders = pipeline->GetShaders(); + ASSERT_EQ(1U, shaders.size()); + + const auto& shader = shaders[0]; + const auto& options = shader.GetCompileOptions(); + ASSERT_EQ(5U, options.size()); + EXPECT_EQ("--option1", options[0]); + EXPECT_EQ("--option2=blah", options[1]); + EXPECT_EQ("other", options[2]); + EXPECT_EQ("--option3", options[3]); + EXPECT_EQ("3", options[4]); +} + +TEST_F(AmberScriptParserTest, PipelineShaderCompileOptionsMissingShader) { + std::string in = R"( +SHADER compute my_shader OPENCL-C +#shader +END +PIPELINE compute my_pipeline + ATTACH my_shader + COMPILE_OPTIONS +END +)"; + + Parser parser; + auto r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("8: missing shader name in COMPILE_OPTIONS command", r.Error()); +} + +TEST_F(AmberScriptParserTest, PipelineShaderCompileOptionsBadShader) { + std::string in = R"( +SHADER compute my_shader OPENCL-C +#shader +END +PIPELINE compute my_pipeline + ATTACH my_shader + COMPILE_OPTIONS not_my_shader + END +END +)"; + + Parser parser; + auto r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("7: unknown shader in COMPILE_OPTIONS command", r.Error()); +} + +TEST_F(AmberScriptParserTest, PipelineShaderCompileOptionsMissingEnd) { + std::string in = R"( +SHADER compute my_shader OPENCL-C +#shader +END +PIPELINE compute my_pipeline + ATTACH my_shader + COMPILE_OPTIONS my_shader +)"; + + Parser parser; + auto r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("8: COMPILE_OPTIONS missing END command", r.Error()); +} + +TEST_F(AmberScriptParserTest, PipelineShaderCompileOptionsExtraToken) { + std::string in = R"( +SHADER compute my_shader OPENCL-C +#shader +END +PIPELINE compute my_pipeline + ATTACH my_shader + COMPILE_OPTIONS my_shader extra + END +END +)"; + + Parser parser; + auto r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("7: extra parameters after COMPILE_OPTIONS command", r.Error()); +} + +TEST_F(AmberScriptParserTest, PipelineShaderCompileOptionsExtraTokenEnd) { + std::string in = R"( +SHADER compute my_shader OPENCL-C +#shader +END +PIPELINE compute my_pipeline + ATTACH my_shader + COMPILE_OPTIONS my_shader + END token +END +)"; + + Parser parser; + auto r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("8: extra parameters after COMPILE_OPTIONS command", r.Error()); +} + +TEST_F(AmberScriptParserTest, PipelineShaderCompileOptionsNotOpenCL) { + std::string in = R"( +SHADER compute my_shader SPIRV-ASM +#shader +END +PIPELINE compute my_pipeline + ATTACH my_shader + COMPILE_OPTIONS my_shader + END token +END +)"; + + Parser parser; + auto r = parser.Parse(in); + ASSERT_FALSE(r.IsSuccess()); + EXPECT_EQ("7: COMPILE_OPTIONS currently only supports OPENCL-C shaders", + r.Error()); +} + +} // namespace amberscript +} // namespace amber diff --git a/src/clspv_helper.cc b/src/clspv_helper.cc index c4ac35a..d0e9f49 100644 --- a/src/clspv_helper.cc +++ b/src/clspv_helper.cc @@ -26,8 +26,12 @@ Result Compile(Pipeline::ShaderInfo* shader_info, std::vector<uint32_t>* generated_binary) { std::vector<clspv::version0::DescriptorMapEntry> entries; const auto& src_str = shader_info->GetShader()->GetData(); - if (clspv::CompileFromSourceString(src_str, "", "", generated_binary, - &entries)) { + std::string options; + for (const auto& option : shader_info->GetCompileOptions()) { + options += option + " "; + } + if (clspv::CompileFromSourceString(src_str, /* sampler map */ "", options, + generated_binary, &entries)) { return Result("Clspv compile failed"); } diff --git a/src/pipeline.cc b/src/pipeline.cc index 08a8469..87ff88b 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -108,6 +108,23 @@ Result Pipeline::SetShaderOptimizations(const Shader* shader, shader->GetName()); } +Result Pipeline::SetShaderCompileOptions(const Shader* shader, + const std::vector<std::string>& opts) { + if (!shader) + return Result("invalid shader specified for compile options"); + + for (auto& info : shaders_) { + const auto* is = info.GetShader(); + if (is == shader) { + info.SetCompileOptions(opts); + return {}; + } + } + + return Result("unknown shader specified for compile options: " + + shader->GetName()); +} + Result Pipeline::SetShaderEntryPoint(const Shader* shader, const std::string& name) { if (!shader) diff --git a/src/pipeline.h b/src/pipeline.h index 7ea8f9a..eab5679 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -49,6 +49,13 @@ class Pipeline { return shader_optimizations_; } + void SetCompileOptions(const std::vector<std::string>& options) { + compile_options_ = options; + } + const std::vector<std::string>& GetCompileOptions() const { + return compile_options_; + } + void SetShader(Shader* shader) { shader_ = shader; } const Shader* GetShader() const { return shader_; } @@ -105,6 +112,7 @@ class Pipeline { std::map<uint32_t, uint32_t> specialization_; std::unordered_map<std::string, std::vector<DescriptorMapEntry>> descriptor_map_; + std::vector<std::string> compile_options_; }; /// Information on a buffer attached to the pipeline. @@ -165,6 +173,9 @@ class Pipeline { /// Sets the optimizations (|opts|) for |shader| in this pipeline. Result SetShaderOptimizations(const Shader* shader, const std::vector<std::string>& opts); + /// Sets the compile options for |shader| in this pipeline. + Result SetShaderCompileOptions(const Shader* shader, + const std::vector<std::string>& options); /// Returns a list of all colour attachments in this pipeline. const std::vector<BufferInfo>& GetColorAttachments() const { diff --git a/src/shader_compiler_test.cc b/src/shader_compiler_test.cc index e33d6dd..dac7d2b 100644 --- a/src/shader_compiler_test.cc +++ b/src/shader_compiler_test.cc @@ -14,6 +14,7 @@ #include "src/shader_compiler.h" +#include <algorithm> #include <string> #include <vector> @@ -272,6 +273,57 @@ kernel void TestShader(global int* in, global int* out) { ASSERT_FALSE(r.IsSuccess()); EXPECT_TRUE(binary.empty()); } + +TEST_F(ShaderCompilerTest, ClspvCompileOptions) { + std::string data = R"( +kernel void TestShader(global int* in, global int* out, int m, int b) { + *out = *in * m + b; +} +)"; + Shader shader(kShaderTypeCompute); + shader.SetName("TestShader"); + shader.SetFormat(kShaderFormatOpenCLC); + shader.SetData(data); + + ShaderCompiler sc; + Result r; + std::vector<uint32_t> binary; + Pipeline::ShaderInfo shader_info1(&shader, kShaderTypeCompute); + std::tie(r, binary) = sc.Compile(&shader_info1, ShaderMap()); + ASSERT_TRUE(r.IsSuccess()); + EXPECT_FALSE(binary.empty()); + EXPECT_EQ(0x07230203, binary[0]); // Verify SPIR-V header present. + auto iter = shader_info1.GetDescriptorMap().find("TestShader"); + ASSERT_NE(iter, shader_info1.GetDescriptorMap().end()); + uint32_t max_binding = 0; + bool has_pod_ubo = 0; + for (const auto& entry : iter->second) { + max_binding = std::max(max_binding, entry.binding); + has_pod_ubo = + entry.kind == Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO; + } + EXPECT_EQ(3U, max_binding); + EXPECT_FALSE(has_pod_ubo); + + binary.clear(); + Pipeline::ShaderInfo shader_info2(&shader, kShaderTypeCompute); + shader_info2.SetCompileOptions({"-cluster-pod-kernel-args", "-pod-ubo"}); + std::tie(r, binary) = sc.Compile(&shader_info2, ShaderMap()); + ASSERT_TRUE(r.IsSuccess()); + EXPECT_FALSE(binary.empty()); + EXPECT_EQ(0x07230203, binary[0]); // Verify SPIR-V header present. + iter = shader_info2.GetDescriptorMap().find("TestShader"); + ASSERT_NE(iter, shader_info2.GetDescriptorMap().end()); + max_binding = 0; + has_pod_ubo = 0; + for (const auto& entry : iter->second) { + max_binding = std::max(max_binding, entry.binding); + has_pod_ubo = + entry.kind == Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO; + } + EXPECT_EQ(2U, max_binding); + EXPECT_TRUE(has_pod_ubo); +} #endif // AMBER_ENABLE_CLSPV struct ParseSpvEnvCase { diff --git a/tests/cases/opencl_set_arg.amber b/tests/cases/opencl_set_arg.amber index 75fb995..cbaa567 100644 --- a/tests/cases/opencl_set_arg.amber +++ b/tests/cases/opencl_set_arg.amber @@ -20,16 +20,59 @@ kernel void line(global int* in, global int* out, int slope, int offset) { END BUFFER in_buf DATA_TYPE uint32 DATA 3 END -BUFFER out_buf DATA_TYPE uint32 DATA 0 END +BUFFER out_buf1 DATA_TYPE uint32 DATA 0 END +BUFFER out_buf2 DATA_TYPE uint32 DATA 0 END +BUFFER out_buf3 DATA_TYPE uint32 DATA 0 END +BUFFER out_buf4 DATA_TYPE uint32 DATA 0 END -PIPELINE compute my_pipeline +PIPELINE compute p1 ATTACH my_shader ENTRY_POINT line BIND BUFFER in_buf KERNEL ARG_NAME in - BIND BUFFER out_buf KERNEL ARG_NAME out + BIND BUFFER out_buf1 KERNEL ARG_NAME out SET KERNEL ARG_NAME offset AS uint32 1 SET KERNEL ARG_NAME slope AS int32 2 END -RUN my_pipeline 1 1 1 +PIPELINE compute p2 + ATTACH my_shader ENTRY_POINT line + BIND BUFFER in_buf KERNEL ARG_NAME in + BIND BUFFER out_buf2 KERNEL ARG_NAME out + SET KERNEL ARG_NAME offset AS uint32 1 + SET KERNEL ARG_NAME slope AS int32 2 + COMPILE_OPTIONS my_shader + -pod-ubo + END +END + +PIPELINE compute p3 + ATTACH my_shader ENTRY_POINT line + BIND BUFFER in_buf KERNEL ARG_NAME in + BIND BUFFER out_buf3 KERNEL ARG_NAME out + SET KERNEL ARG_NAME offset AS uint32 1 + SET KERNEL ARG_NAME slope AS int32 2 + COMPILE_OPTIONS my_shader + -cluster-pod-kernel-args + END +END + +PIPELINE compute p4 + ATTACH my_shader ENTRY_POINT line + BIND BUFFER in_buf KERNEL ARG_NAME in + BIND BUFFER out_buf4 KERNEL ARG_NAME out + SET KERNEL ARG_NAME offset AS uint32 1 + SET KERNEL ARG_NAME slope AS int32 2 + COMPILE_OPTIONS my_shader + -cluster-pod-kernel-args + -pod-ubo + END +END + +RUN p1 1 1 1 +RUN p2 1 1 1 +RUN p3 1 1 1 +RUN p4 1 1 1 -EXPECT out_buf IDX 0 EQ 7 +EXPECT out_buf1 IDX 0 EQ 7 +EXPECT out_buf2 IDX 0 EQ 7 +EXPECT out_buf3 IDX 0 EQ 7 +EXPECT out_buf4 IDX 0 EQ 7 |