aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralan-baker <alanbaker@google.com>2019-07-23 08:32:36 -0400
committerGitHub <noreply@github.com>2019-07-23 08:32:36 -0400
commit1c7b905284129c4a9f2f735e08da6f53b4eb4d9b (patch)
tree860dfc404a066f9a37d6ca639afdebaea6a46359
parentd7fc00b52faa239546eefd1e74f9fd2e3b25b993 (diff)
downloadamber-1c7b905284129c4a9f2f735e08da6f53b4eb4d9b.tar.gz
Opencl set for kernel args (#589)
Fixes #428 * Adds parsing (and tests) for SET command in pipeline command: * only for use with OPENCL-C shaders (errors out in other cases) ``` SET KERNEL ARG_NAME _name_ AS {datum_type} _value_ SET KERNEL ARG_NUMBER _number_ AS {datum_type} _value_ ``` * Executor now invokes the pipeline to generate buffers for the PoD args populated from SET commands * Allow clustered pod args * refactor opencl specific code in executor to its own loop * Add end-to-end test * Document SET command
-rw-r--r--docs/amber_script.md14
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/amberscript/parser.cc65
-rw-r--r--src/amberscript/parser.h1
-rw-r--r--src/amberscript/parser_pipeline_set_test.cc245
-rw-r--r--src/clspv_helper.cc2
-rw-r--r--src/executor.cc7
-rw-r--r--src/pipeline.cc122
-rw-r--r--src/pipeline.h23
-rw-r--r--src/pipeline_test.cc170
-rw-r--r--tests/cases/opencl_set_arg.amber35
11 files changed, 679 insertions, 6 deletions
diff --git a/docs/amber_script.md b/docs/amber_script.md
index a7a552c..93e97ba 100644
--- a/docs/amber_script.md
+++ b/docs/amber_script.md
@@ -275,6 +275,20 @@ attachment content, depth/stencil content, uniform buffers, etc.
INDEX_DATA {buffer_name}
```
+##### OpenCL Plain-Old-Data Arguments
+OpenCL kernels can have plain-old-data (pod or pod_ubo in the desriptor map)
+arguments set their data via this command. Amber will generate the appropriate
+buffers for the pipeline populated with the specified data.
+
+```groovy
+ # Set argument |name| to |data_type| with value |val|.
+ SET KERNEL ARG_NAME _name_ AS {data_type} _val_
+
+ # Set argument |number| to |data_type| with value |val|.
+ # Arguments use 0-based numbering.
+ SET KERNEL ARG_NUMBER _number_ AS {data_type} _val_
+```
+
##### Topologies
* `point_list`
* `line_list`
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 03d183a..cdf5c47 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -123,6 +123,7 @@ if (${AMBER_ENABLE_TESTS})
amberscript/parser_extension_test.cc
amberscript/parser_framebuffer_test.cc
amberscript/parser_pipeline_test.cc
+ amberscript/parser_pipeline_set_test.cc
amberscript/parser_repeat_test.cc
amberscript/parser_run_test.cc
amberscript/parser_set_test.cc
diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc
index 1abe3a4..82b8987 100644
--- a/src/amberscript/parser.cc
+++ b/src/amberscript/parser.cc
@@ -415,6 +415,8 @@ Result Parser::ParsePipelineBody(const std::string& cmd_name,
r = ParsePipelineVertexData(pipeline.get());
} else if (tok == "INDEX_DATA") {
r = ParsePipelineIndexData(pipeline.get());
+ } else if (tok == "SET") {
+ r = ParsePipelineSet(pipeline.get());
} else {
r = Result("unknown token in pipeline block: " + tok);
}
@@ -778,6 +780,69 @@ Result Parser::ParsePipelineIndexData(Pipeline* pipeline) {
return ValidateEndOfStatement("INDEX_DATA command");
}
+Result Parser::ParsePipelineSet(Pipeline* pipeline) {
+ if (pipeline->GetShaders().empty() ||
+ pipeline->GetShaders()[0].GetShader()->GetFormat() !=
+ kShaderFormatOpenCLC) {
+ return Result("SET can only be used with OPENCL-C shaders");
+ }
+
+ auto token = tokenizer_->NextToken();
+ if (!token->IsString() || token->AsString() != "KERNEL")
+ return Result("missing KERNEL in SET command");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsString())
+ return Result("expected ARG_NAME or ARG_NUMBER");
+
+ std::string arg_name = "";
+ uint32_t arg_no = std::numeric_limits<uint32_t>::max();
+ if (token->AsString() == "ARG_NAME") {
+ token = tokenizer_->NextToken();
+ if (!token->IsString())
+ return Result("expected argument identifier");
+ arg_name = token->AsString();
+ } else if (token->AsString() == "ARG_NUMBER") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected argument number");
+ arg_no = token->AsUint32();
+ } else {
+ return Result("expected ARG_NAME or ARG_NUMBER");
+ }
+
+ token = tokenizer_->NextToken();
+ if (!token->IsString() || token->AsString() != "AS")
+ return Result("missing AS in SET command");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsString())
+ return Result("expected data type");
+
+ DatumType arg_type;
+ auto r = ToDatumType(token->AsString(), &arg_type);
+ if (!r.IsSuccess())
+ return r;
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger() && !token->IsDouble())
+ return Result("expected data value");
+
+ Value value;
+ if (arg_type.IsFloat() || arg_type.IsDouble())
+ value.SetDoubleValue(token->AsDouble());
+ else
+ value.SetIntValue(token->AsUint64());
+
+ Pipeline::ArgSetInfo info;
+ info.name = arg_name;
+ info.ordinal = arg_no;
+ info.type = arg_type;
+ info.value = value;
+ pipeline->SetArg(std::move(info));
+ return ValidateEndOfStatement("SET command");
+}
+
Result Parser::ParseBuffer() {
auto token = tokenizer_->NextToken();
if (!token->IsString())
diff --git a/src/amberscript/parser.h b/src/amberscript/parser.h
index f5d7b46..028ae5c 100644
--- a/src/amberscript/parser.h
+++ b/src/amberscript/parser.h
@@ -62,6 +62,7 @@ class Parser : public amber::Parser {
Result ParsePipelineBind(Pipeline*);
Result ParsePipelineVertexData(Pipeline*);
Result ParsePipelineIndexData(Pipeline*);
+ Result ParsePipelineSet(Pipeline*);
Result ParseRun();
Result ParseClear();
Result ParseClearColor();
diff --git a/src/amberscript/parser_pipeline_set_test.cc b/src/amberscript/parser_pipeline_set_test.cc
new file mode 100644
index 0000000..b35b35e
--- /dev/null
+++ b/src/amberscript/parser_pipeline_set_test.cc
@@ -0,0 +1,245 @@
+// 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, OpenCLSetMissingKernel) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET ARG_NAME a AS uint32 0
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: missing KERNEL in SET command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetMissingArgName) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL a AS uint32 0
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: expected ARG_NAME or ARG_NUMBER", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetMissingArgIdentifier) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NAME AS uint32 0
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: missing AS in SET command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetMissingArgIdentifierNumber) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NUMBER AS uint32 0
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: expected argument number", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetMissingAs) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NAME a uint32 0
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: missing AS in SET command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetMissingDataType) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NAME a AS 0
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: expected data type", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetMissingDataValue) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NAME a AS uint32
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("8: expected data value", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetExtraTokens) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NAME a AS uint32 0 BLAH
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: extra parameters after SET command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetArgNameNotString) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NAME 0 AS uint32 0
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: expected argument identifier", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetArgNumberNotInteger) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NUMBER 1.0 AS uint32 0
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: expected argument number", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetDataTypeNotString) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NUMBER 0 AS 0 0
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: expected data type", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetDataValueString) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NUMBER 0 AS uint32 data
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: expected data value", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, OpenCLSetWrongShaderFormat) {
+ std::string in = R"(
+SHADER compute my_shader SPIRV-ASM
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NAME arg_a AS uint32 0
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: SET can only be used with OPENCL-C shaders", r.Error());
+}
+
+} // namespace amberscript
+} // namespace amber
diff --git a/src/clspv_helper.cc b/src/clspv_helper.cc
index e0d853e..c4ac35a 100644
--- a/src/clspv_helper.cc
+++ b/src/clspv_helper.cc
@@ -68,8 +68,6 @@ Result Compile(Pipeline::ShaderInfo* shader_info,
if (entry.kernel_arg_data.arg_kind == clspv::ArgKind::Pod ||
entry.kernel_arg_data.arg_kind == clspv::ArgKind::PodUBO) {
- if (entry.kernel_arg_data.pod_offset != 0)
- return Result("Clustered PoD arguments are not currently supported");
descriptor_entry.pod_offset = entry.kernel_arg_data.pod_offset;
descriptor_entry.pod_arg_size = entry.kernel_arg_data.pod_arg_size;
}
diff --git a/src/executor.cc b/src/executor.cc
index 3bbec7a..1c3dd89 100644
--- a/src/executor.cc
+++ b/src/executor.cc
@@ -59,10 +59,17 @@ Result Executor::Execute(Engine* engine,
if (!r.IsSuccess())
return r;
+ // OpenCL specific pipeline updates.
for (auto& pipeline : script->GetPipelines()) {
r = pipeline->UpdateOpenCLBufferBindings();
if (!r.IsSuccess())
return r;
+ r = pipeline->GenerateOpenCLPodBuffers();
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ for (auto& pipeline : script->GetPipelines()) {
r = engine->CreatePipeline(pipeline.get());
if (!r.IsSuccess())
return r;
diff --git a/src/pipeline.cc b/src/pipeline.cc
index 8f214f0..08a8469 100644
--- a/src/pipeline.cc
+++ b/src/pipeline.cc
@@ -430,4 +430,126 @@ Result Pipeline::UpdateOpenCLBufferBindings() {
return {};
}
+Result Pipeline::GenerateOpenCLPodBuffers() {
+ if (!IsCompute() || GetShaders().empty() ||
+ GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
+ return {};
+ }
+
+ const auto& shader_info = GetShaders()[0];
+ const auto& descriptor_map = shader_info.GetDescriptorMap();
+ if (descriptor_map.empty())
+ return {};
+
+ const auto iter = descriptor_map.find(shader_info.GetEntryPoint());
+ if (iter == descriptor_map.end())
+ return {};
+
+ // For each SET command, do the following:
+ // 1. Find the descriptor map entry for that argument.
+ // 2. Find or create the buffer for the descriptor set and binding pair.
+ // 3. Write the data for the SET command at the right offset.
+ for (const auto& arg_info : SetArgValues()) {
+ uint32_t descriptor_set = std::numeric_limits<uint32_t>::max();
+ uint32_t binding = std::numeric_limits<uint32_t>::max();
+ uint32_t offset = 0;
+ uint32_t arg_size = 0;
+ bool uses_name = !arg_info.name.empty();
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind kind =
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
+ for (const auto& entry : iter->second) {
+ if (entry.kind != Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD &&
+ entry.kind !=
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO) {
+ continue;
+ }
+
+ // Found the right entry.
+ if ((uses_name && entry.arg_name == arg_info.name) ||
+ entry.arg_ordinal == arg_info.ordinal) {
+ descriptor_set = entry.descriptor_set;
+ binding = entry.binding;
+ offset = entry.pod_offset;
+ arg_size = entry.pod_arg_size;
+ kind = entry.kind;
+ break;
+ }
+ }
+
+ if (descriptor_set == std::numeric_limits<uint32_t>::max() ||
+ binding == std::numeric_limits<uint32_t>::max()) {
+ std::string message =
+ "could not find descriptor map entry for SET command: kernel " +
+ shader_info.GetEntryPoint();
+ if (uses_name) {
+ message += ", name " + arg_info.name;
+ } else {
+ message += ", number " + std::to_string(arg_info.ordinal);
+ }
+ return Result(message);
+ }
+
+ auto buf_iter = opencl_pod_buffer_map_.lower_bound(
+ std::make_pair(descriptor_set, binding));
+ Buffer* buffer = nullptr;
+ if (buf_iter == opencl_pod_buffer_map_.end() ||
+ buf_iter->first.first != descriptor_set ||
+ buf_iter->first.second != binding) {
+ // Ensure no buffer was previously bound for this descriptor set and
+ // binding pair.
+ for (const auto& buf_info : GetBuffers()) {
+ if (buf_info.descriptor_set == descriptor_set &&
+ buf_info.binding == binding) {
+ return Result("previously bound buffer " +
+ buf_info.buffer->GetName() +
+ " to PoD args at descriptor set " +
+ std::to_string(descriptor_set) + " binding " +
+ std::to_string(binding));
+ }
+ }
+
+ // Add a new buffer for this descriptor set and binding.
+ opencl_pod_buffers_.push_back(MakeUnique<Buffer>());
+ buffer = opencl_pod_buffers_.back().get();
+ buffer->SetBufferType(
+ kind == Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD
+ ? BufferType::kStorage
+ : BufferType::kUniform);
+ // Use an 8-bit type because all the data in the descriptor map is
+ // byte-based and it simplifies the logic for sizing below.
+ DatumType char_type;
+ char_type.SetType(DataType::kUint8);
+ buffer->SetFormat(char_type.AsFormat());
+ buffer->SetName(GetName() + "_pod_buffer_" +
+ std::to_string(descriptor_set) + "_" +
+ std::to_string(binding));
+ opencl_pod_buffer_map_.insert(
+ buf_iter,
+ std::make_pair(std::make_pair(descriptor_set, binding), buffer));
+ AddBuffer(buffer, descriptor_set, binding);
+ } else {
+ buffer = buf_iter->second;
+ }
+
+ // Resize if necessary.
+ if (buffer->ValueCount() < offset + arg_size) {
+ buffer->ResizeTo(offset + arg_size);
+ }
+ // Check the data size.
+ if (arg_size != arg_info.type.SizeInBytes()) {
+ std::string message = "SET command uses incorrect data size: kernel " +
+ shader_info.GetEntryPoint();
+ if (uses_name) {
+ message += ", name " + arg_info.name;
+ } else {
+ message += ", number " + std::to_string(arg_info.ordinal);
+ }
+ return Result(message);
+ }
+ buffer->SetDataWithOffset({arg_info.value}, offset);
+ }
+
+ return {};
+}
+
} // namespace amber
diff --git a/src/pipeline.h b/src/pipeline.h
index 8859888..7ea8f9a 100644
--- a/src/pipeline.h
+++ b/src/pipeline.h
@@ -226,6 +226,24 @@ class Pipeline {
/// Generates a default depth attachment in D32_SFLOAT_S8_UINT format.
std::unique_ptr<Buffer> GenerateDefaultDepthAttachmentBuffer() const;
+ /// Information on values set for OpenCL-C plain-old-data args.
+ struct ArgSetInfo {
+ std::string name;
+ uint32_t ordinal = 0;
+ DatumType type;
+ Value value;
+ };
+
+ /// Adds value from SET command.
+ void SetArg(ArgSetInfo&& info) { set_arg_values_.push_back(std::move(info)); }
+ const std::vector<ArgSetInfo>& SetArgValues() const {
+ return set_arg_values_;
+ }
+
+ /// Generate the buffers necessary for OpenCL PoD arguments populated via SET
+ /// command. This should be called after all other buffers are bound.
+ Result GenerateOpenCLPodBuffers();
+
private:
void UpdateFramebufferSizes();
@@ -244,6 +262,11 @@ class Pipeline {
uint32_t fb_width_ = 250;
uint32_t fb_height_ = 250;
+
+ std::vector<ArgSetInfo> set_arg_values_;
+ std::vector<std::unique_ptr<Buffer>> opencl_pod_buffers_;
+ /// Maps (descriptor set, binding) to the buffer for that binding pair.
+ std::map<std::pair<uint32_t, uint32_t>, Buffer*> opencl_pod_buffer_map_;
};
} // namespace amber
diff --git a/src/pipeline_test.cc b/src/pipeline_test.cc
index 8e0ddcc..8a0d2a5 100644
--- a/src/pipeline_test.cc
+++ b/src/pipeline_test.cc
@@ -396,8 +396,7 @@ TEST_F(PipelineTest, Clone) {
EXPECT_EQ(2U, bufs[1].binding);
}
-#if AMBER_ENABLE_CLSPV
-TEST_F(PipelineTest, ClspvUpdateBindings) {
+TEST_F(PipelineTest, OpenCLUpdateBindings) {
Pipeline p(PipelineType::kCompute);
p.SetName("my_pipeline");
@@ -442,7 +441,7 @@ TEST_F(PipelineTest, ClspvUpdateBindings) {
EXPECT_EQ(1U, bufs[1].binding);
}
-TEST_F(PipelineTest, ClspvUpdateBindingTypeMismatch) {
+TEST_F(PipelineTest, OpenCLUpdateBindingTypeMismatch) {
Pipeline p(PipelineType::kCompute);
p.SetName("my_pipeline");
@@ -480,6 +479,169 @@ TEST_F(PipelineTest, ClspvUpdateBindingTypeMismatch) {
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("Buffer buf2 must be an uniform binding", r.Error());
}
-#endif // AMBER_ENABLE_CLSPV
+
+TEST_F(PipelineTest, OpenCLGeneratePodBuffers) {
+ Pipeline p(PipelineType::kCompute);
+ p.SetName("my_pipeline");
+
+ Shader cs(kShaderTypeCompute);
+ cs.SetFormat(kShaderFormatOpenCLC);
+ p.AddShader(&cs, kShaderTypeCompute);
+ p.SetShaderEntryPoint(&cs, "my_main");
+
+ // Descriptor map.
+ Pipeline::ShaderInfo::DescriptorMapEntry entry1;
+ entry1.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
+ entry1.descriptor_set = 4;
+ entry1.binding = 5;
+ entry1.arg_name = "arg_a";
+ entry1.arg_ordinal = 0;
+ entry1.pod_offset = 0;
+ entry1.pod_arg_size = 4;
+ p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
+
+ Pipeline::ShaderInfo::DescriptorMapEntry entry2;
+ entry2.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
+ entry2.descriptor_set = 4;
+ entry2.binding = 5;
+ entry2.arg_name = "arg_b";
+ entry2.arg_ordinal = 0;
+ entry2.pod_offset = 4;
+ entry2.pod_arg_size = 1;
+ p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
+
+ Pipeline::ShaderInfo::DescriptorMapEntry entry3;
+ entry3.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
+ entry3.descriptor_set = 4;
+ entry3.binding = 4;
+ entry3.arg_name = "arg_c";
+ entry3.arg_ordinal = 0;
+ entry3.pod_offset = 0;
+ entry3.pod_arg_size = 4;
+ p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry3));
+
+ // Set commands.
+ Value int_value;
+ int_value.SetIntValue(1);
+ DatumType int_type;
+ int_type.SetType(DataType::kInt32);
+ DatumType char_type;
+ char_type.SetType(DataType::kInt8);
+
+ Pipeline::ArgSetInfo arg_info1;
+ arg_info1.name = "arg_a";
+ arg_info1.ordinal = 99;
+ arg_info1.type = int_type;
+ arg_info1.value = int_value;
+ p.SetArg(std::move(arg_info1));
+
+ Pipeline::ArgSetInfo arg_info2;
+ arg_info2.name = "arg_b";
+ arg_info2.ordinal = 99;
+ arg_info2.type = char_type;
+ arg_info2.value = int_value;
+ p.SetArg(std::move(arg_info2));
+
+ Pipeline::ArgSetInfo arg_info3;
+ arg_info3.name = "arg_c";
+ arg_info3.ordinal = 99;
+ arg_info3.type = int_type;
+ arg_info3.value = int_value;
+ p.SetArg(std::move(arg_info3));
+
+ auto r = p.GenerateOpenCLPodBuffers();
+ ASSERT_TRUE(r.IsSuccess());
+ EXPECT_EQ(2U, p.GetBuffers().size());
+
+ const auto& b1 = p.GetBuffers()[0];
+ EXPECT_EQ(4U, b1.descriptor_set);
+ EXPECT_EQ(5U, b1.binding);
+ EXPECT_EQ(5U, b1.buffer->ValueCount());
+
+ const auto& b2 = p.GetBuffers()[1];
+ EXPECT_EQ(4U, b2.descriptor_set);
+ EXPECT_EQ(4U, b2.binding);
+ EXPECT_EQ(4U, b2.buffer->ValueCount());
+}
+
+TEST_F(PipelineTest, OpenCLGeneratePodBuffersBadName) {
+ Pipeline p(PipelineType::kCompute);
+ p.SetName("my_pipeline");
+
+ Shader cs(kShaderTypeCompute);
+ cs.SetFormat(kShaderFormatOpenCLC);
+ p.AddShader(&cs, kShaderTypeCompute);
+ p.SetShaderEntryPoint(&cs, "my_main");
+
+ // Descriptor map.
+ Pipeline::ShaderInfo::DescriptorMapEntry entry1;
+ entry1.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
+ entry1.descriptor_set = 4;
+ entry1.binding = 5;
+ entry1.arg_name = "arg_a";
+ entry1.arg_ordinal = 0;
+ entry1.pod_offset = 0;
+ entry1.pod_arg_size = 4;
+ p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
+
+ // Set commands.
+ Value int_value;
+ int_value.SetIntValue(1);
+ DatumType int_type;
+ int_type.SetType(DataType::kInt32);
+
+ Pipeline::ArgSetInfo arg_info1;
+ arg_info1.name = "arg_z";
+ arg_info1.ordinal = 99;
+ arg_info1.type = int_type;
+ arg_info1.value = int_value;
+ p.SetArg(std::move(arg_info1));
+
+ auto r = p.GenerateOpenCLPodBuffers();
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "could not find descriptor map entry for SET command: kernel my_main, "
+ "name arg_z",
+ r.Error());
+}
+
+TEST_F(PipelineTest, OpenCLGeneratePodBuffersBadSize) {
+ Pipeline p(PipelineType::kCompute);
+ p.SetName("my_pipeline");
+
+ Shader cs(kShaderTypeCompute);
+ cs.SetFormat(kShaderFormatOpenCLC);
+ p.AddShader(&cs, kShaderTypeCompute);
+ p.SetShaderEntryPoint(&cs, "my_main");
+
+ // Descriptor map.
+ Pipeline::ShaderInfo::DescriptorMapEntry entry1;
+ entry1.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
+ entry1.descriptor_set = 4;
+ entry1.binding = 5;
+ entry1.arg_name = "arg_a";
+ entry1.arg_ordinal = 0;
+ entry1.pod_offset = 0;
+ entry1.pod_arg_size = 4;
+ p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
+
+ // Set commands.
+ Value int_value;
+ int_value.SetIntValue(1);
+ DatumType short_type;
+ short_type.SetType(DataType::kInt16);
+
+ Pipeline::ArgSetInfo arg_info1;
+ arg_info1.name = "";
+ arg_info1.ordinal = 0;
+ arg_info1.type = short_type;
+ arg_info1.value = int_value;
+ p.SetArg(std::move(arg_info1));
+
+ auto r = p.GenerateOpenCLPodBuffers();
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("SET command uses incorrect data size: kernel my_main, number 0",
+ r.Error());
+}
} // namespace amber
diff --git a/tests/cases/opencl_set_arg.amber b/tests/cases/opencl_set_arg.amber
new file mode 100644
index 0000000..75fb995
--- /dev/null
+++ b/tests/cases/opencl_set_arg.amber
@@ -0,0 +1,35 @@
+#!amber
+# 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
+#
+# https://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 implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute my_shader OPENCL-C
+kernel void line(global int* in, global int* out, int slope, int offset) {
+ *out = *in * slope + offset;
+}
+END
+
+BUFFER in_buf DATA_TYPE uint32 DATA 3 END
+BUFFER out_buf DATA_TYPE uint32 DATA 0 END
+
+PIPELINE compute my_pipeline
+ ATTACH my_shader ENTRY_POINT line
+ BIND BUFFER in_buf KERNEL ARG_NAME in
+ BIND BUFFER out_buf 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
+
+EXPECT out_buf IDX 0 EQ 7