blob: 4db30558d85f8779bfd7f0aca5f09c08bd40b8b0 [file] [log] [blame]
// Copyright 2018 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 implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "amber/amber.h"
#include <cctype>
#include <cstdlib>
#include <memory>
#include <string>
#include "src/amberscript/parser.h"
#include "src/descriptor_set_and_binding_parser.h"
#include "src/engine.h"
#include "src/executor.h"
#include "src/make_unique.h"
#include "src/parser.h"
#include "src/vkscript/parser.h"
namespace amber {
namespace {
const FormatType kDefaultFramebufferFormat = FormatType::kB8G8R8A8_UNORM;
Result GetFrameBuffer(Buffer* buffer, std::vector<Value>* values) {
values->clear();
// TODO(jaebaek): Support other formats
if (buffer->GetFormat()->GetFormatType() != kDefaultFramebufferFormat)
return Result("GetFrameBuffer Unsupported buffer format");
const uint8_t* cpu_memory = buffer->ValuePtr()->data();
if (!cpu_memory)
return Result("GetFrameBuffer missing memory pointer");
const auto texel_stride = buffer->GetElementStride();
const auto row_stride = buffer->GetRowStride();
for (uint32_t y = 0; y < buffer->GetHeight(); ++y) {
for (uint32_t x = 0; x < buffer->GetWidth(); ++x) {
Value pixel;
const uint8_t* ptr_8 = cpu_memory + (row_stride * y) + (texel_stride * x);
const uint32_t* ptr_32 = reinterpret_cast<const uint32_t*>(ptr_8);
pixel.SetIntValue(*ptr_32);
values->push_back(pixel);
}
}
return {};
}
} // namespace
EngineConfig::~EngineConfig() = default;
Options::Options()
: engine(amber::EngineType::kEngineTypeVulkan),
config(nullptr),
execution_type(ExecutionType::kExecute),
disable_spirv_validation(false),
delegate(nullptr) {}
Options::~Options() = default;
BufferInfo::BufferInfo() : is_image_buffer(false), width(0), height(0) {}
BufferInfo::BufferInfo(const BufferInfo&) = default;
BufferInfo::~BufferInfo() = default;
BufferInfo& BufferInfo::operator=(const BufferInfo&) = default;
Delegate::~Delegate() = default;
Amber::Amber() = default;
Amber::~Amber() = default;
amber::Result Amber::Parse(const std::string& input, amber::Recipe* recipe) {
if (!recipe)
return Result("Recipe must be provided to Parse.");
std::unique_ptr<Parser> parser;
if (input.substr(0, 7) == "#!amber")
parser = MakeUnique<amberscript::Parser>();
else
parser = MakeUnique<vkscript::Parser>();
Result r = parser->Parse(input);
if (!r.IsSuccess())
return r;
recipe->SetImpl(parser->GetScript().release());
return {};
}
namespace {
// Create an engine initialize it, and check the recipe's requirements.
// Returns a failing result if anything fails. Otherwise pass the created
// engine out through |engine_ptr| and the script via |script|. The |script|
// pointer is borrowed, and should not be freed.
Result CreateEngineAndCheckRequirements(const Recipe* recipe,
Options* opts,
std::unique_ptr<Engine>* engine_ptr,
Script** script_ptr) {
if (!recipe)
return Result("Attempting to check an invalid recipe");
Script* script = static_cast<Script*>(recipe->GetImpl());
if (!script)
return Result("Recipe must contain a parsed script");
script->SetSpvTargetEnv(opts->spv_env);
auto engine = Engine::Create(opts->engine);
if (!engine) {
return Result("Failed to create engine");
}
// Engine initialization checks requirements. Current backends don't do
// much else. Refactor this if they end up doing to much here.
Result r = engine->Initialize(opts->config, opts->delegate,
script->GetRequiredFeatures(),
script->GetRequiredInstanceExtensions(),
script->GetRequiredDeviceExtensions());
if (!r.IsSuccess())
return r;
*engine_ptr = std::move(engine);
*script_ptr = script;
return r;
}
} // namespace
amber::Result Amber::AreAllRequirementsSupported(const amber::Recipe* recipe,
Options* opts) {
std::unique_ptr<Engine> engine;
Script* script = nullptr;
return CreateEngineAndCheckRequirements(recipe, opts, &engine, &script);
}
amber::Result Amber::Execute(const amber::Recipe* recipe, Options* opts) {
ShaderMap map;
return ExecuteWithShaderData(recipe, opts, map);
}
amber::Result Amber::ExecuteWithShaderData(const amber::Recipe* recipe,
Options* opts,
const ShaderMap& shader_data) {
std::unique_ptr<Engine> engine;
Script* script = nullptr;
Result r = CreateEngineAndCheckRequirements(recipe, opts, &engine, &script);
if (!r.IsSuccess())
return r;
script->SetSpvTargetEnv(opts->spv_env);
Executor executor;
Result executor_result =
executor.Execute(engine.get(), script, shader_data, opts);
// Hold the executor result until the extractions are complete. This will let
// us dump any buffers requested even on failure.
if (script->GetPipelines().empty()) {
if (!executor_result.IsSuccess())
return executor_result;
return {};
}
// TODO(dsinclair): Figure out how extractions work with multiple pipelines.
auto* pipeline = script->GetPipelines()[0].get();
// The dump process holds onto the results and terminates the loop if any dump
// fails. This will allow us to validate |extractor_result| first as if the
// extractor fails before running the pipeline that will trigger the dumps
// to almost always fail.
for (BufferInfo& buffer_info : opts->extractions) {
if (buffer_info.is_image_buffer) {
auto* buffer = script->GetBuffer(buffer_info.buffer_name);
if (!buffer)
break;
buffer_info.width = buffer->GetWidth();
buffer_info.height = buffer->GetHeight();
r = GetFrameBuffer(buffer, &(buffer_info.values));
if (!r.IsSuccess())
break;
continue;
}
DescriptorSetAndBindingParser desc_set_and_binding_parser;
r = desc_set_and_binding_parser.Parse(buffer_info.buffer_name);
if (!r.IsSuccess())
break;
const auto* buffer = pipeline->GetBufferForBinding(
desc_set_and_binding_parser.GetDescriptorSet(),
desc_set_and_binding_parser.GetBinding());
if (!buffer)
break;
const uint8_t* ptr = buffer->ValuePtr()->data();
auto& values = buffer_info.values;
for (size_t i = 0; i < buffer->GetSizeInBytes(); ++i) {
values.emplace_back();
values.back().SetIntValue(*ptr);
++ptr;
}
}
if (!executor_result.IsSuccess())
return executor_result;
if (!r.IsSuccess())
return r;
return {};
}
} // namespace amber