1 // Copyright 2018 The Amber Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "amber/amber.h"
16
17 #include <cctype>
18 #include <cstdlib>
19 #include <memory>
20 #include <string>
21
22 #include "src/amberscript/parser.h"
23 #include "src/descriptor_set_and_binding_parser.h"
24 #include "src/engine.h"
25 #include "src/executor.h"
26 #include "src/make_unique.h"
27 #include "src/parser.h"
28 #include "src/vkscript/parser.h"
29
30 namespace amber {
31 namespace {
32
33 const FormatType kDefaultFramebufferFormat = FormatType::kB8G8R8A8_UNORM;
34
GetFrameBuffer(Buffer * buffer,std::vector<Value> * values)35 Result GetFrameBuffer(Buffer* buffer, std::vector<Value>* values) {
36 values->clear();
37
38 // TODO(jaebaek): Support other formats
39 if (buffer->GetFormat()->GetFormatType() != kDefaultFramebufferFormat)
40 return Result("GetFrameBuffer Unsupported buffer format");
41
42 const uint8_t* cpu_memory = buffer->ValuePtr()->data();
43 if (!cpu_memory)
44 return Result("GetFrameBuffer missing memory pointer");
45
46 const auto texel_stride = buffer->GetElementStride();
47 const auto row_stride = buffer->GetRowStride();
48
49 for (uint32_t y = 0; y < buffer->GetHeight(); ++y) {
50 for (uint32_t x = 0; x < buffer->GetWidth(); ++x) {
51 Value pixel;
52
53 const uint8_t* ptr_8 = cpu_memory + (row_stride * y) + (texel_stride * x);
54 const uint32_t* ptr_32 = reinterpret_cast<const uint32_t*>(ptr_8);
55 pixel.SetIntValue(*ptr_32);
56 values->push_back(pixel);
57 }
58 }
59
60 return {};
61 }
62
63 } // namespace
64
65 EngineConfig::~EngineConfig() = default;
66
Options()67 Options::Options()
68 : engine(amber::EngineType::kEngineTypeVulkan),
69 config(nullptr),
70 execution_type(ExecutionType::kExecute),
71 disable_spirv_validation(false) {}
72
73 Options::~Options() = default;
74
BufferInfo()75 BufferInfo::BufferInfo() : is_image_buffer(false), width(0), height(0) {}
76
77 BufferInfo::BufferInfo(const BufferInfo&) = default;
78
79 BufferInfo::~BufferInfo() = default;
80
81 BufferInfo& BufferInfo::operator=(const BufferInfo&) = default;
82
83 Delegate::~Delegate() = default;
84
Amber(Delegate * delegate)85 Amber::Amber(Delegate* delegate) : delegate_(delegate) {}
86
87 Amber::~Amber() = default;
88
Parse(const std::string & input,amber::Recipe * recipe)89 amber::Result Amber::Parse(const std::string& input, amber::Recipe* recipe) {
90 if (!recipe)
91 return Result("Recipe must be provided to Parse.");
92
93 std::unique_ptr<Parser> parser;
94 if (input.substr(0, 7) == "#!amber")
95 parser = MakeUnique<amberscript::Parser>(GetDelegate());
96 else
97 parser = MakeUnique<vkscript::Parser>(GetDelegate());
98
99 Result r = parser->Parse(input);
100 if (!r.IsSuccess())
101 return r;
102
103 recipe->SetImpl(parser->GetScript().release());
104 return {};
105 }
106
107 namespace {
108
109 // Create an engine initialize it, and check the recipe's requirements.
110 // Returns a failing result if anything fails. Otherwise pass the created
111 // engine out through |engine_ptr| and the script via |script|. The |script|
112 // pointer is borrowed, and should not be freed.
CreateEngineAndCheckRequirements(const Recipe * recipe,Options * opts,Delegate * delegate,std::unique_ptr<Engine> * engine_ptr,Script ** script_ptr)113 Result CreateEngineAndCheckRequirements(const Recipe* recipe,
114 Options* opts,
115 Delegate* delegate,
116 std::unique_ptr<Engine>* engine_ptr,
117 Script** script_ptr) {
118 if (!recipe)
119 return Result("Attempting to check an invalid recipe");
120
121 Script* script = static_cast<Script*>(recipe->GetImpl());
122 if (!script)
123 return Result("Recipe must contain a parsed script");
124
125 script->SetSpvTargetEnv(opts->spv_env);
126
127 auto engine = Engine::Create(opts->engine);
128 if (!engine) {
129 return Result("Failed to create engine");
130 }
131
132 // Engine initialization checks requirements. Current backends don't do
133 // much else. Refactor this if they end up doing to much here.
134 Result r =
135 engine->Initialize(opts->config, delegate, script->GetRequiredFeatures(),
136 script->GetRequiredInstanceExtensions(),
137 script->GetRequiredDeviceExtensions());
138 if (!r.IsSuccess())
139 return r;
140
141 *engine_ptr = std::move(engine);
142 *script_ptr = script;
143
144 return r;
145 }
146 } // namespace
147
AreAllRequirementsSupported(const amber::Recipe * recipe,Options * opts)148 amber::Result Amber::AreAllRequirementsSupported(const amber::Recipe* recipe,
149 Options* opts) {
150 std::unique_ptr<Engine> engine;
151 Script* script = nullptr;
152
153 return CreateEngineAndCheckRequirements(recipe, opts, GetDelegate(), &engine,
154 &script);
155 }
156
Execute(const amber::Recipe * recipe,Options * opts)157 amber::Result Amber::Execute(const amber::Recipe* recipe, Options* opts) {
158 ShaderMap map;
159 return ExecuteWithShaderData(recipe, opts, map);
160 }
161
ExecuteWithShaderData(const amber::Recipe * recipe,Options * opts,const ShaderMap & shader_data)162 amber::Result Amber::ExecuteWithShaderData(const amber::Recipe* recipe,
163 Options* opts,
164 const ShaderMap& shader_data) {
165 std::unique_ptr<Engine> engine;
166 Script* script = nullptr;
167 Result r = CreateEngineAndCheckRequirements(recipe, opts, GetDelegate(),
168 &engine, &script);
169 if (!r.IsSuccess())
170 return r;
171 script->SetSpvTargetEnv(opts->spv_env);
172
173 Executor executor;
174 Result executor_result =
175 executor.Execute(engine.get(), script, shader_data, opts, GetDelegate());
176 // Hold the executor result until the extractions are complete. This will let
177 // us dump any buffers requested even on failure.
178
179 if (script->GetPipelines().empty()) {
180 if (!executor_result.IsSuccess())
181 return executor_result;
182 return {};
183 }
184
185 // Try to perform each extraction, copying the buffer data into |buffer_info|.
186 // We do not overwrite |executor_result| if extraction fails.
187 for (BufferInfo& buffer_info : opts->extractions) {
188 if (buffer_info.is_image_buffer) {
189 auto* buffer = script->GetBuffer(buffer_info.buffer_name);
190 if (!buffer)
191 continue;
192
193 buffer_info.width = buffer->GetWidth();
194 buffer_info.height = buffer->GetHeight();
195 GetFrameBuffer(buffer, &(buffer_info.values));
196 continue;
197 }
198
199 DescriptorSetAndBindingParser p;
200 r = p.Parse(buffer_info.buffer_name);
201 if (!r.IsSuccess())
202 continue;
203
204 // Extract the named pipeline from the request, otherwise use the
205 // first pipeline which was parsed.
206 Pipeline* pipeline = nullptr;
207 if (p.HasPipelineName())
208 pipeline = script->GetPipeline(p.PipelineName());
209 else
210 pipeline = script->GetPipelines()[0].get();
211
212 const auto* buffer =
213 pipeline->GetBufferForBinding(p.GetDescriptorSet(), p.GetBinding());
214 if (!buffer)
215 continue;
216
217 const uint8_t* ptr = buffer->ValuePtr()->data();
218 auto& values = buffer_info.values;
219 for (size_t i = 0; i < buffer->GetSizeInBytes(); ++i) {
220 values.emplace_back();
221 values.back().SetIntValue(*ptr);
222 ++ptr;
223 }
224 }
225
226 if (!executor_result.IsSuccess())
227 return executor_result;
228 if (!r.IsSuccess())
229 return r;
230
231 return {};
232 }
233
234 } // namespace amber
235