aboutsummaryrefslogtreecommitdiff
path: root/android_sample/jni
diff options
context:
space:
mode:
authorJaebaek Seo <duke.acacia@gmail.com>2019-01-10 17:43:21 -0500
committerGitHub <noreply@github.com>2019-01-10 17:43:21 -0500
commitd6a750e0fe966516e9b799bba55fb295e124a81b (patch)
tree1ff91cd882500a1444da04e20f6d1a34ac6503e9 /android_sample/jni
parent610296cc045b094509a6bd2f420ebdef1f98edf9 (diff)
downloadamber-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.cc157
-rw-r--r--android_sample/jni/amber_script.h75
-rw-r--r--android_sample/jni/main.cc106
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);
+ }
+ }
+}