diff options
author | Jaebaek Seo <duke.acacia@gmail.com> | 2019-01-10 17:43:21 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-10 17:43:21 -0500 |
commit | d6a750e0fe966516e9b799bba55fb295e124a81b (patch) | |
tree | 1ff91cd882500a1444da04e20f6d1a34ac6503e9 /android_sample/jni | |
parent | 610296cc045b094509a6bd2f420ebdef1f98edf9 (diff) | |
download | amber-d6a750e0fe966516e9b799bba55fb295e124a81b.tar.gz |
Add a sample Android app (#183)
Fixes #179
Diffstat (limited to 'android_sample/jni')
-rw-r--r-- | android_sample/jni/amber_script.cc | 157 | ||||
-rw-r--r-- | android_sample/jni/amber_script.h | 75 | ||||
-rw-r--r-- | android_sample/jni/main.cc | 106 |
3 files changed, 338 insertions, 0 deletions
diff --git a/android_sample/jni/amber_script.cc b/android_sample/jni/amber_script.cc new file mode 100644 index 0000000..2d00716 --- /dev/null +++ b/android_sample/jni/amber_script.cc @@ -0,0 +1,157 @@ +// 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 implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "amber_script.h" + +#include "src/make_unique.h" + +namespace amber { +namespace android { +namespace { + +const char kAmberDir[] = "amber/"; +const char kAmberScriptExtension[] = ".amber"; +const char kShaderNameSignature[] = ".vk_shader_"; +const char kShaderExtension[] = ".spv"; + +bool IsEndedWith(const std::string& path, const std::string& end) { + const size_t path_size = path.size(); + const size_t end_size = end.size(); + if (path_size < end_size) + return false; + + return path.compare(path_size - end_size, end_size, end) == 0; +} + +bool IsStartedWith(const std::string& path, const std::string& start) { + const size_t path_size = path.size(); + const size_t start_size = start.size(); + if (path_size < start_size) + return false; + + return path.compare(0, start_size, start) == 0; +} + +std::string GetShaderID(const std::string& shader_name) { + size_t spv_extension_pos = shader_name.find_last_of('.'); + if (spv_extension_pos == std::string::npos) + return std::string(); + + size_t shader_id_pos = + shader_name.find_last_of('.', spv_extension_pos - 1UL) + 1UL; + if (shader_id_pos == std::string::npos) + return std::string(); + + if (shader_id_pos >= spv_extension_pos || shader_name.size() <= shader_id_pos) + return std::string(); + + return shader_name.substr(shader_id_pos, spv_extension_pos - shader_id_pos); +} + +} // namespace + +AmberScriptLoader::AmberScriptLoader(android_app* app) : app_context_(app) {} + +AmberScriptLoader::~AmberScriptLoader() = default; + +Result AmberScriptLoader::LoadAllScriptsFromAsset() { + auto shader_names = FindAllScriptsAndReturnShaderNames(); + if (script_info_.empty()) + return Result("No Amber script found"); + + for (auto& info : script_info_) { + info.script_content = ReadScript(info.asset_name); + if (info.script_content.empty()) + return Result(info.asset_name + ":\n\tEmpty Amber script"); + } + + for (auto& info : script_info_) { + for (const auto& shader : shader_names) { + if (!IsStartedWith(shader, info.asset_name + kShaderNameSignature)) + continue; + + auto shader_content = ReadSpvShader(shader); + if (shader_content.empty()) + return Result(shader + ":\n\tEmpty shader"); + + auto id = GetShaderID(shader); + if (id.empty()) + return Result(shader + ":\n\tFail to get shader ID"); + + info.shader_map[id] = shader_content; + } + } + + return {}; +} + +std::vector<std::string> +AmberScriptLoader::FindAllScriptsAndReturnShaderNames() { + std::vector<std::string> shaders; + + AAssetDir* asset = + AAssetManager_openDir(app_context_->activity->assetManager, kAmberDir); + for (const char* file_name = AAssetDir_getNextFileName(asset); file_name; + file_name = AAssetDir_getNextFileName(asset)) { + std::string file_name_in_string(file_name); + if (IsEndedWith(file_name_in_string, kAmberScriptExtension)) { + script_info_.emplace_back(); + script_info_.back().asset_name = file_name_in_string; + } + + if (IsEndedWith(file_name_in_string, kShaderExtension)) + shaders.push_back(file_name_in_string); + } + AAssetDir_close(asset); + + return shaders; +} + +std::vector<uint8_t> AmberScriptLoader::ReadContent( + const std::string& asset_name) { + auto asset_path = kAmberDir + asset_name; + AAsset* asset = AAssetManager_open(app_context_->activity->assetManager, + asset_path.c_str(), AASSET_MODE_BUFFER); + if (!asset) + return std::vector<uint8_t>(); + + size_t size_in_bytes = AAsset_getLength(asset); + + // Allocate a memory chunk whose size in bytes is |size_in_bytes|. + std::vector<uint8_t> content(size_in_bytes); + + AAsset_read(asset, content.data(), size_in_bytes); + AAsset_close(asset); + + return content; +} + +std::string AmberScriptLoader::ReadScript(const std::string& script_name) { + auto content = ReadContent(script_name); + return std::string(reinterpret_cast<char*>(content.data())); +} + +std::vector<uint32_t> AmberScriptLoader::ReadSpvShader( + const std::string& shader_name) { + auto content = ReadContent(shader_name); + if (content.size() % sizeof(uint32_t) != 0) + return std::vector<uint32_t>(); + + return std::vector<uint32_t>( + reinterpret_cast<uint32_t*>(content.data()), + reinterpret_cast<uint32_t*>(content.data() + content.size())); +} + +} // namespace android +} // namespace amber diff --git a/android_sample/jni/amber_script.h b/android_sample/jni/amber_script.h new file mode 100644 index 0000000..495555b --- /dev/null +++ b/android_sample/jni/amber_script.h @@ -0,0 +1,75 @@ +// 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 implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ANDROID_AMBER_SCRIPT_H_ +#define ANDROID_AMBER_SCRIPT_H_ + +#include <android_native_app_glue.h> +#include <memory> +#include <string> +#include <vector> + +#include "amber/amber.h" +#include "amber/result.h" + +namespace amber { +namespace android { + +struct AmberScriptInfo { + std::string asset_name; // Script asset name. Note it is not a + // path and just the name of script file. + std::string script_content; // Script itself from the script file. + amber::ShaderMap shader_map; +}; + +// A class to load scripts for Amber under assets/amber/ into +// |script_info_|. We assume that file extension of those scripts +// is ".amber" and all files with the extension are scripts for +// Amber. +class AmberScriptLoader { + public: + explicit AmberScriptLoader(android_app* app); + ~AmberScriptLoader(); + + Result LoadAllScriptsFromAsset(); + const std::vector<AmberScriptInfo>& GetScripts() const { + return script_info_; + } + + private: + // Find all files with ".amber" extension and set |asset_name| of + // |script_info_| as their names. In addition, return all shader + // file names that have ".spv" extensions. + std::vector<std::string> FindAllScriptsAndReturnShaderNames(); + + // Return content of script named |script_name| under + // assets/amber/ as a std::string. + std::string ReadScript(const std::string& script_name); + + // Return SPIRV binary of script named |shader_name| under + // assets/amber/ as a std::vector<uint32_t>. + std::vector<uint32_t> ReadSpvShader(const std::string& shader_name); + + // Return content of asset named |asset_name| under assets/amber/ + // as a std::vector<uint8_t>. + std::vector<uint8_t> ReadContent(const std::string& asset_name); + + android_app* app_context_ = nullptr; + std::vector<AmberScriptInfo> script_info_; +}; + +} // namespace android +} // namespace amber + +#endif // ANDROID_AMBER_SCRIPT_H_ diff --git a/android_sample/jni/main.cc b/android_sample/jni/main.cc new file mode 100644 index 0000000..77d3157 --- /dev/null +++ b/android_sample/jni/main.cc @@ -0,0 +1,106 @@ +// 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 implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <android/log.h> +#include <android_native_app_glue.h> + +#include "amber/amber.h" +#include "amber/recipe.h" +#include "amber/result.h" +#include "amber_script.h" + +namespace { + +// TODO(jaebaek): Change this as a method rather than macro. +// Android log function wrappers +const char* kTAG = "Amber"; +#define LOGE(...) \ + ((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, __VA_ARGS__)) + +void amber_sample_main(android_app* app) { + amber::android::AmberScriptLoader loader(app); + + amber::Result r = loader.LoadAllScriptsFromAsset(); + if (!r.IsSuccess()) { + LOGE("%s", r.Error().c_str()); + return; + } + + const auto& script_info = loader.GetScripts(); + + std::vector<std::string> failures; + for (const auto& info : script_info) { + LOGE("\ncase %s: run...", info.asset_name.c_str()); + + amber::Amber am; + amber::Recipe recipe; + amber::Result r = am.Parse(info.script_content, &recipe); + if (!r.IsSuccess()) { + LOGE("\ncase %s: fail\n\t%s", info.asset_name.c_str(), r.Error().c_str()); + failures.push_back(info.asset_name); + continue; + } + + amber::Options amber_options; + r = am.ExecuteWithShaderData(&recipe, amber_options, info.shader_map); + if (!r.IsSuccess()) { + LOGE("\ncase %s: fail\n\t%s", info.asset_name.c_str(), r.Error().c_str()); + failures.push_back(info.asset_name); + continue; + } + + LOGE("\ncase %s: pass", info.asset_name.c_str()); + } + + if (!failures.empty()) { + LOGE("\nSummary of Failures:"); + for (const auto& failure : failures) + LOGE("%s", failure.c_str()); + } + LOGE("\nsummary: %u pass, %u fail", + static_cast<uint32_t>(script_info.size() - failures.size()), + static_cast<uint32_t>(failures.size())); +} + +// Process the next main command. +void handle_cmd(android_app* app, int32_t cmd) { + switch (cmd) { + case APP_CMD_INIT_WINDOW: + amber_sample_main(app); + break; + case APP_CMD_TERM_WINDOW: + break; + default: + break; + } +} + +} // namespace + +void android_main(struct android_app* app) { + // Set the callback to process system events + app->onAppCmd = handle_cmd; + + // Used to poll the events in the main loop + int events; + android_poll_source* source; + + // Main loop + while (app->destroyRequested == 0) { + if (ALooper_pollAll(1, nullptr, &events, (void**)&source) >= 0) { + if (source != NULL) + source->process(app, source); + } + } +} |