1 //
2 // Copyright 2021 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // frame_capture_test_utils:
7 // Helper functions for capture and replay of traces.
8 //
9
10 #include "frame_capture_test_utils.h"
11
12 #include "common/frame_capture_utils.h"
13 #include "common/string_utils.h"
14
15 #include <rapidjson/document.h>
16 #include <rapidjson/istreamwrapper.h>
17 #include <fstream>
18
19 namespace angle
20 {
21 namespace
22 {
LoadJSONFromFile(const std::string & fileName,rapidjson::Document * doc)23 bool LoadJSONFromFile(const std::string &fileName, rapidjson::Document *doc)
24 {
25 std::ifstream ifs(fileName);
26 if (!ifs.is_open())
27 {
28 return false;
29 }
30
31 rapidjson::IStreamWrapper inWrapper(ifs);
32 doc->ParseStream(inWrapper);
33 return !doc->HasParseError();
34 }
35 } // namespace
36
LoadTraceNamesFromJSON(const std::string jsonFilePath,std::vector<std::string> * namesOut)37 bool LoadTraceNamesFromJSON(const std::string jsonFilePath, std::vector<std::string> *namesOut)
38 {
39 rapidjson::Document doc;
40 if (!LoadJSONFromFile(jsonFilePath, &doc))
41 {
42 return false;
43 }
44
45 if (!doc.IsObject() || !doc.HasMember("traces") || !doc["traces"].IsArray())
46 {
47 return false;
48 }
49
50 // Read trace json into a list of trace names.
51 std::vector<std::string> traces;
52
53 rapidjson::Document::Array traceArray = doc["traces"].GetArray();
54 for (rapidjson::SizeType arrayIndex = 0; arrayIndex < traceArray.Size(); ++arrayIndex)
55 {
56 const rapidjson::Document::ValueType &arrayElement = traceArray[arrayIndex];
57
58 if (!arrayElement.IsString())
59 {
60 return false;
61 }
62
63 std::vector<std::string> traceAndVersion;
64 angle::SplitStringAlongWhitespace(arrayElement.GetString(), &traceAndVersion);
65 traces.push_back(traceAndVersion[0]);
66 }
67
68 *namesOut = std::move(traces);
69 return true;
70 }
71
LoadTraceInfoFromJSON(const std::string & traceName,const std::string & traceJsonPath,TraceInfo * traceInfoOut)72 bool LoadTraceInfoFromJSON(const std::string &traceName,
73 const std::string &traceJsonPath,
74 TraceInfo *traceInfoOut)
75 {
76 rapidjson::Document doc;
77 if (!LoadJSONFromFile(traceJsonPath, &doc))
78 {
79 return false;
80 }
81
82 if (!doc.IsObject() || !doc.HasMember("TraceMetadata"))
83 {
84 return false;
85 }
86
87 const rapidjson::Document::Object &meta = doc["TraceMetadata"].GetObj();
88
89 strncpy(traceInfoOut->name, traceName.c_str(), kTraceInfoMaxNameLen);
90 traceInfoOut->contextClientMajorVersion = meta["ContextClientMajorVersion"].GetInt();
91 traceInfoOut->contextClientMinorVersion = meta["ContextClientMinorVersion"].GetInt();
92 traceInfoOut->frameEnd = meta["FrameEnd"].GetInt();
93 traceInfoOut->frameStart = meta["FrameStart"].GetInt();
94 traceInfoOut->drawSurfaceHeight = meta["DrawSurfaceHeight"].GetInt();
95 traceInfoOut->drawSurfaceWidth = meta["DrawSurfaceWidth"].GetInt();
96
97 angle::HexStringToUInt(meta["DrawSurfaceColorSpace"].GetString(),
98 &traceInfoOut->drawSurfaceColorSpace);
99 angle::HexStringToUInt(meta["DisplayPlatformType"].GetString(),
100 &traceInfoOut->displayPlatformType);
101 angle::HexStringToUInt(meta["DisplayDeviceType"].GetString(), &traceInfoOut->displayDeviceType);
102
103 traceInfoOut->configRedBits = meta["ConfigRedBits"].GetInt();
104 traceInfoOut->configGreenBits = meta["ConfigGreenBits"].GetInt();
105 traceInfoOut->configBlueBits = meta["ConfigBlueBits"].GetInt();
106 traceInfoOut->configAlphaBits = meta["ConfigAlphaBits"].GetInt();
107 traceInfoOut->configDepthBits = meta["ConfigDepthBits"].GetInt();
108 traceInfoOut->configStencilBits = meta["ConfigStencilBits"].GetInt();
109
110 traceInfoOut->isBinaryDataCompressed = meta["IsBinaryDataCompressed"].GetBool();
111 traceInfoOut->areClientArraysEnabled = meta["AreClientArraysEnabled"].GetBool();
112 traceInfoOut->isBindGeneratesResourcesEnabled =
113 meta["IsBindGeneratesResourcesEnabled"].GetBool();
114 traceInfoOut->isWebGLCompatibilityEnabled = meta["IsWebGLCompatibilityEnabled"].GetBool();
115 traceInfoOut->isRobustResourceInitEnabled = meta["IsRobustResourceInitEnabled"].GetBool();
116 traceInfoOut->windowSurfaceContextId = doc["WindowSurfaceContextID"].GetInt();
117
118 if (doc.HasMember("RequiredExtensions"))
119 {
120 const rapidjson::Value &requiredExtensions = doc["RequiredExtensions"];
121 if (!requiredExtensions.IsArray())
122 {
123 return false;
124 }
125 for (rapidjson::SizeType i = 0; i < requiredExtensions.Size(); i++)
126 {
127 std::string ext = std::string(requiredExtensions[i].GetString());
128 traceInfoOut->requiredExtensions.push_back(ext);
129 }
130 }
131
132 if (meta.HasMember("KeyFrames"))
133 {
134 const rapidjson::Value &keyFrames = meta["KeyFrames"];
135 if (!keyFrames.IsArray())
136 {
137 return false;
138 }
139 for (rapidjson::SizeType i = 0; i < keyFrames.Size(); i++)
140 {
141 int frame = keyFrames[i].GetInt();
142 traceInfoOut->keyFrames.push_back(frame);
143 }
144 }
145
146 const rapidjson::Document::Array &traceFiles = doc["TraceFiles"].GetArray();
147 for (const rapidjson::Value &value : traceFiles)
148 {
149 traceInfoOut->traceFiles.push_back(value.GetString());
150 }
151
152 traceInfoOut->initialized = true;
153 return true;
154 }
155
TraceLibrary(const std::string & traceName,const TraceInfo & traceInfo)156 TraceLibrary::TraceLibrary(const std::string &traceName, const TraceInfo &traceInfo)
157 {
158 std::stringstream libNameStr;
159 SearchType searchType = SearchType::ModuleDir;
160
161 #if defined(ANGLE_TRACE_EXTERNAL_BINARIES)
162 // This means we are using the binary build of traces on Android, which are
163 // not bundled in the APK, but located in the app's home directory.
164 searchType = SearchType::SystemDir;
165 libNameStr << "/data/user/0/com.android.angle.test/angle_traces/";
166 #endif // defined(ANGLE_TRACE_EXTERNAL_BINARIES)
167 #if !defined(ANGLE_PLATFORM_WINDOWS)
168 libNameStr << "lib";
169 #endif // !defined(ANGLE_PLATFORM_WINDOWS)
170 libNameStr << traceName;
171 #if defined(ANGLE_PLATFORM_ANDROID) && defined(COMPONENT_BUILD)
172 // Added to shared library names in Android component builds in
173 // https://chromium.googlesource.com/chromium/src/+/9bacc8c4868cc802f69e1e858eea6757217a508f/build/toolchain/toolchain.gni#56
174 libNameStr << ".cr";
175 #endif // defined(ANGLE_PLATFORM_ANDROID) && defined(COMPONENT_BUILD)
176 std::string libName = libNameStr.str();
177 std::string loadError;
178 mTraceLibrary.reset(OpenSharedLibraryAndGetError(libName.c_str(), searchType, &loadError));
179 if (mTraceLibrary->getNative() == nullptr)
180 {
181 FATAL() << "Failed to load trace library (" << libName << "): " << loadError;
182 }
183
184 callFunc<SetupEntryPoints>("SetupEntryPoints", static_cast<angle::TraceCallbacks *>(this),
185 &mTraceFunctions);
186 mTraceFunctions->SetTraceInfo(traceInfo);
187 mTraceInfo = traceInfo;
188 }
189
LoadBinaryData(const char * fileName)190 uint8_t *TraceLibrary::LoadBinaryData(const char *fileName)
191 {
192 std::ostringstream pathBuffer;
193 pathBuffer << mBinaryDataDir << "/" << fileName;
194 FILE *fp = fopen(pathBuffer.str().c_str(), "rb");
195 if (fp == 0)
196 {
197 fprintf(stderr, "Error loading binary data file: %s\n", fileName);
198 exit(1);
199 }
200 fseek(fp, 0, SEEK_END);
201 long size = ftell(fp);
202 fseek(fp, 0, SEEK_SET);
203 if (mTraceInfo.isBinaryDataCompressed)
204 {
205 if (!strstr(fileName, ".gz"))
206 {
207 fprintf(stderr, "Filename does not end in .gz");
208 exit(1);
209 }
210
211 std::vector<uint8_t> compressedData(size);
212 (void)fread(compressedData.data(), 1, size, fp);
213
214 uint32_t uncompressedSize =
215 zlib_internal::GetGzipUncompressedSize(compressedData.data(), compressedData.size());
216
217 mBinaryData.resize(uncompressedSize + 1); // +1 to make sure .data() is valid
218 uLong destLen = uncompressedSize;
219 int zResult =
220 zlib_internal::GzipUncompressHelper(mBinaryData.data(), &destLen, compressedData.data(),
221 static_cast<uLong>(compressedData.size()));
222
223 if (zResult != Z_OK)
224 {
225 std::cerr << "Failure to decompressed binary data: " << zResult << "\n";
226 exit(1);
227 }
228 }
229 else
230 {
231 if (!strstr(fileName, ".angledata"))
232 {
233 fprintf(stderr, "Filename does not end in .angledata");
234 exit(1);
235 }
236 mBinaryData.resize(size + 1);
237 (void)fread(mBinaryData.data(), 1, size, fp);
238 }
239 fclose(fp);
240
241 return mBinaryData.data();
242 }
243
244 } // namespace angle
245