• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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       delegate(nullptr) {}
73 
74 Options::~Options() = default;
75 
BufferInfo()76 BufferInfo::BufferInfo() : is_image_buffer(false), width(0), height(0) {}
77 
78 BufferInfo::BufferInfo(const BufferInfo&) = default;
79 
80 BufferInfo::~BufferInfo() = default;
81 
82 BufferInfo& BufferInfo::operator=(const BufferInfo&) = default;
83 
84 Delegate::~Delegate() = default;
85 
86 Amber::Amber() = default;
87 
88 Amber::~Amber() = default;
89 
Parse(const std::string & input,amber::Recipe * recipe)90 amber::Result Amber::Parse(const std::string& input, amber::Recipe* recipe) {
91   if (!recipe)
92     return Result("Recipe must be provided to Parse.");
93 
94   std::unique_ptr<Parser> parser;
95   if (input.substr(0, 7) == "#!amber")
96     parser = MakeUnique<amberscript::Parser>();
97   else
98     parser = MakeUnique<vkscript::Parser>();
99 
100   Result r = parser->Parse(input);
101   if (!r.IsSuccess())
102     return r;
103 
104   recipe->SetImpl(parser->GetScript().release());
105   return {};
106 }
107 
108 namespace {
109 
110 // Create an engine initialize it, and check the recipe's requirements.
111 // Returns a failing result if anything fails.  Otherwise pass the created
112 // engine out through |engine_ptr| and the script via |script|.  The |script|
113 // pointer is borrowed, and should not be freed.
CreateEngineAndCheckRequirements(const Recipe * recipe,Options * opts,std::unique_ptr<Engine> * engine_ptr,Script ** script_ptr)114 Result CreateEngineAndCheckRequirements(const Recipe* recipe,
115                                         Options* opts,
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 = engine->Initialize(opts->config, opts->delegate,
135                                 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, &engine, &script);
154 }
155 
Execute(const amber::Recipe * recipe,Options * opts)156 amber::Result Amber::Execute(const amber::Recipe* recipe, Options* opts) {
157   ShaderMap map;
158   return ExecuteWithShaderData(recipe, opts, map);
159 }
160 
ExecuteWithShaderData(const amber::Recipe * recipe,Options * opts,const ShaderMap & shader_data)161 amber::Result Amber::ExecuteWithShaderData(const amber::Recipe* recipe,
162                                            Options* opts,
163                                            const ShaderMap& shader_data) {
164   std::unique_ptr<Engine> engine;
165   Script* script = nullptr;
166   Result r = CreateEngineAndCheckRequirements(recipe, opts, &engine, &script);
167   if (!r.IsSuccess())
168     return r;
169   script->SetSpvTargetEnv(opts->spv_env);
170 
171   Executor executor;
172   Result executor_result =
173       executor.Execute(engine.get(), script, shader_data, opts);
174   // Hold the executor result until the extractions are complete. This will let
175   // us dump any buffers requested even on failure.
176 
177   if (script->GetPipelines().empty()) {
178     if (!executor_result.IsSuccess())
179       return executor_result;
180     return {};
181   }
182 
183   // TODO(dsinclair): Figure out how extractions work with multiple pipelines.
184   auto* pipeline = script->GetPipelines()[0].get();
185 
186   // The dump process holds onto the results and terminates the loop if any dump
187   // fails. This will allow us to validate |extractor_result| first as if the
188   // extractor fails before running the pipeline that will trigger the dumps
189   // to almost always fail.
190   for (BufferInfo& buffer_info : opts->extractions) {
191     if (buffer_info.is_image_buffer) {
192       auto* buffer = script->GetBuffer(buffer_info.buffer_name);
193       if (!buffer)
194         break;
195 
196       buffer_info.width = buffer->GetWidth();
197       buffer_info.height = buffer->GetHeight();
198       r = GetFrameBuffer(buffer, &(buffer_info.values));
199       if (!r.IsSuccess())
200         break;
201 
202       continue;
203     }
204 
205     DescriptorSetAndBindingParser desc_set_and_binding_parser;
206     r = desc_set_and_binding_parser.Parse(buffer_info.buffer_name);
207     if (!r.IsSuccess())
208       break;
209 
210     const auto* buffer = pipeline->GetBufferForBinding(
211         desc_set_and_binding_parser.GetDescriptorSet(),
212         desc_set_and_binding_parser.GetBinding());
213     if (!buffer)
214       break;
215 
216     const uint8_t* ptr = buffer->ValuePtr()->data();
217     auto& values = buffer_info.values;
218     for (size_t i = 0; i < buffer->GetSizeInBytes(); ++i) {
219       values.emplace_back();
220       values.back().SetIntValue(*ptr);
221       ++ptr;
222     }
223   }
224 
225   if (!executor_result.IsSuccess())
226     return executor_result;
227   if (!r.IsSuccess())
228     return r;
229 
230   return {};
231 }
232 
233 }  // namespace amber
234