diff options
author | alan-baker <alanbaker@google.com> | 2019-07-23 08:32:36 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-23 08:32:36 -0400 |
commit | 1c7b905284129c4a9f2f735e08da6f53b4eb4d9b (patch) | |
tree | 860dfc404a066f9a37d6ca639afdebaea6a46359 | |
parent | d7fc00b52faa239546eefd1e74f9fd2e3b25b993 (diff) | |
download | amber-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.md | 14 | ||||
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/amberscript/parser.cc | 65 | ||||
-rw-r--r-- | src/amberscript/parser.h | 1 | ||||
-rw-r--r-- | src/amberscript/parser_pipeline_set_test.cc | 245 | ||||
-rw-r--r-- | src/clspv_helper.cc | 2 | ||||
-rw-r--r-- | src/executor.cc | 7 | ||||
-rw-r--r-- | src/pipeline.cc | 122 | ||||
-rw-r--r-- | src/pipeline.h | 23 | ||||
-rw-r--r-- | src/pipeline_test.cc | 170 | ||||
-rw-r--r-- | tests/cases/opencl_set_arg.amber | 35 |
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 |