1 // Copyright 2019 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_script.h"
16
17 #include "src/make_unique.h"
18
19 namespace amber {
20 namespace android {
21 namespace {
22
23 const char kAmberDir[] = "amber/";
24 const char kAmberScriptExtension[] = ".amber";
25 const char kShaderNameSignature[] = ".vk_shader_";
26 const char kShaderExtension[] = ".spv";
27
IsEndedWith(const std::string & path,const std::string & end)28 bool IsEndedWith(const std::string& path, const std::string& end) {
29 const size_t path_size = path.size();
30 const size_t end_size = end.size();
31 if (path_size < end_size)
32 return false;
33
34 return path.compare(path_size - end_size, end_size, end) == 0;
35 }
36
IsStartedWith(const std::string & path,const std::string & start)37 bool IsStartedWith(const std::string& path, const std::string& start) {
38 const size_t path_size = path.size();
39 const size_t start_size = start.size();
40 if (path_size < start_size)
41 return false;
42
43 return path.compare(0, start_size, start) == 0;
44 }
45
GetShaderID(const std::string & shader_name)46 std::string GetShaderID(const std::string& shader_name) {
47 size_t spv_extension_pos = shader_name.find_last_of('.');
48 if (spv_extension_pos == std::string::npos)
49 return std::string();
50
51 size_t shader_id_pos =
52 shader_name.find_last_of('.', spv_extension_pos - 1UL) + 1UL;
53 if (shader_id_pos == std::string::npos)
54 return std::string();
55
56 if (shader_id_pos >= spv_extension_pos || shader_name.size() <= shader_id_pos)
57 return std::string();
58
59 return shader_name.substr(shader_id_pos, spv_extension_pos - shader_id_pos);
60 }
61
62 } // namespace
63
AmberScriptLoader(android_app * app)64 AmberScriptLoader::AmberScriptLoader(android_app* app) : app_context_(app) {}
65
66 AmberScriptLoader::~AmberScriptLoader() = default;
67
LoadAllScriptsFromAsset()68 Result AmberScriptLoader::LoadAllScriptsFromAsset() {
69 auto shader_names = FindAllScriptsAndReturnShaderNames();
70 if (script_info_.empty())
71 return Result("No Amber script found");
72
73 for (auto& info : script_info_) {
74 info.script_content = ReadScript(info.asset_name);
75 if (info.script_content.empty())
76 return Result(info.asset_name + ":\n\tEmpty Amber script");
77 }
78
79 for (auto& info : script_info_) {
80 for (const auto& shader : shader_names) {
81 if (!IsStartedWith(shader, info.asset_name + kShaderNameSignature))
82 continue;
83
84 auto shader_content = ReadSpvShader(shader);
85 if (shader_content.empty())
86 return Result(shader + ":\n\tEmpty shader");
87
88 auto id = GetShaderID(shader);
89 if (id.empty())
90 return Result(shader + ":\n\tFail to get shader ID");
91
92 info.shader_map[id] = shader_content;
93 }
94 }
95
96 return {};
97 }
98
99 std::vector<std::string>
FindAllScriptsAndReturnShaderNames()100 AmberScriptLoader::FindAllScriptsAndReturnShaderNames() {
101 std::vector<std::string> shaders;
102
103 AAssetDir* asset =
104 AAssetManager_openDir(app_context_->activity->assetManager, kAmberDir);
105 for (const char* file_name = AAssetDir_getNextFileName(asset); file_name;
106 file_name = AAssetDir_getNextFileName(asset)) {
107 std::string file_name_in_string(file_name);
108 if (IsEndedWith(file_name_in_string, kAmberScriptExtension)) {
109 script_info_.emplace_back();
110 script_info_.back().asset_name = file_name_in_string;
111 }
112
113 if (IsEndedWith(file_name_in_string, kShaderExtension))
114 shaders.push_back(file_name_in_string);
115 }
116 AAssetDir_close(asset);
117
118 return shaders;
119 }
120
ReadContent(const std::string & asset_name)121 std::vector<uint8_t> AmberScriptLoader::ReadContent(
122 const std::string& asset_name) {
123 auto asset_path = kAmberDir + asset_name;
124 AAsset* asset = AAssetManager_open(app_context_->activity->assetManager,
125 asset_path.c_str(), AASSET_MODE_BUFFER);
126 if (!asset)
127 return std::vector<uint8_t>();
128
129 size_t size_in_bytes = AAsset_getLength(asset);
130
131 // Allocate a memory chunk whose size in bytes is |size_in_bytes|.
132 std::vector<uint8_t> content(size_in_bytes);
133
134 AAsset_read(asset, content.data(), size_in_bytes);
135 AAsset_close(asset);
136
137 return content;
138 }
139
ReadScript(const std::string & script_name)140 std::string AmberScriptLoader::ReadScript(const std::string& script_name) {
141 auto content = ReadContent(script_name);
142 return std::string(reinterpret_cast<char*>(content.data()));
143 }
144
ReadSpvShader(const std::string & shader_name)145 std::vector<uint32_t> AmberScriptLoader::ReadSpvShader(
146 const std::string& shader_name) {
147 auto content = ReadContent(shader_name);
148 if (content.size() % sizeof(uint32_t) != 0)
149 return std::vector<uint32_t>();
150
151 return std::vector<uint32_t>(
152 reinterpret_cast<uint32_t*>(content.data()),
153 reinterpret_cast<uint32_t*>(content.data() + content.size()));
154 }
155
156 } // namespace android
157 } // namespace amber
158