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