aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralan-baker <alanbaker@google.com>2019-07-23 14:50:34 -0400
committerGitHub <noreply@github.com>2019-07-23 14:50:34 -0400
commit7bc793fa18d8f8a518492ed75ed414683c704236 (patch)
tree63996f212526d1cdc3372e85d636587a58fed5f5
parent1c7b905284129c4a9f2f735e08da6f53b4eb4d9b (diff)
downloadamber-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.md38
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/amberscript/parser.cc39
-rw-r--r--src/amberscript/parser.h1
-rw-r--r--src/amberscript/parser_compile_options_test.cc168
-rw-r--r--src/clspv_helper.cc8
-rw-r--r--src/pipeline.cc17
-rw-r--r--src/pipeline.h11
-rw-r--r--src/shader_compiler_test.cc52
-rw-r--r--tests/cases/opencl_set_arg.amber53
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