1 //
2 // Copyright 2020 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 // CaptureReplayTest.cpp:
7 // Application that runs replay for testing of capture replay
8 //
9
10 #include "common/debug.h"
11 #include "common/system_utils.h"
12 #include "util/EGLPlatformParameters.h"
13 #include "util/EGLWindow.h"
14 #include "util/OSWindow.h"
15
16 #include <stdint.h>
17 #include <string.h>
18 #include <fstream>
19 #include <functional>
20 #include <iostream>
21 #include <list>
22 #include <memory>
23 #include <ostream>
24 #include <string>
25 #include <utility>
26
27 #include "util/capture/frame_capture_test_utils.h"
28
29 constexpr char kResultTag[] = "*RESULT";
30 constexpr char kTracePath[] = ANGLE_CAPTURE_REPLAY_TEST_NAMES_PATH;
31
32 class CaptureReplayTests
33 {
34 public:
CaptureReplayTests()35 CaptureReplayTests()
36 {
37 // Load EGL library so we can initialize the display.
38 mEntryPointsLib.reset(
39 angle::OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, angle::SearchType::ModuleDir));
40
41 mOSWindow = OSWindow::New();
42 mOSWindow->disableErrorMessageDialog();
43 }
44
~CaptureReplayTests()45 ~CaptureReplayTests()
46 {
47 EGLWindow::Delete(&mEGLWindow);
48 OSWindow::Delete(&mOSWindow);
49 }
50
initializeTest(const std::string & execDir,const angle::TraceInfo & traceInfo)51 bool initializeTest(const std::string &execDir, const angle::TraceInfo &traceInfo)
52 {
53 if (!mOSWindow->initialize(traceInfo.name, traceInfo.drawSurfaceWidth,
54 traceInfo.drawSurfaceHeight))
55 {
56 return false;
57 }
58
59 mOSWindow->disableErrorMessageDialog();
60 mOSWindow->setVisible(true);
61
62 if (mEGLWindow && !mEGLWindow->isContextVersion(traceInfo.contextClientMajorVersion,
63 traceInfo.contextClientMinorVersion))
64 {
65 EGLWindow::Delete(&mEGLWindow);
66 mEGLWindow = nullptr;
67 }
68
69 if (!mEGLWindow)
70 {
71 mEGLWindow = EGLWindow::New(traceInfo.contextClientMajorVersion,
72 traceInfo.contextClientMinorVersion);
73 }
74
75 ConfigParameters configParams;
76 configParams.redBits = traceInfo.configRedBits;
77 configParams.greenBits = traceInfo.configGreenBits;
78 configParams.blueBits = traceInfo.configBlueBits;
79 configParams.alphaBits = traceInfo.configAlphaBits;
80 configParams.depthBits = traceInfo.configDepthBits;
81 configParams.stencilBits = traceInfo.configStencilBits;
82
83 configParams.clientArraysEnabled = traceInfo.areClientArraysEnabled;
84 configParams.bindGeneratesResource = traceInfo.isBindGeneratesResourcesEnabled;
85 configParams.webGLCompatibility = traceInfo.isWebGLCompatibilityEnabled;
86 configParams.robustResourceInit = traceInfo.isRobustResourceInitEnabled;
87
88 mPlatformParams.renderer = traceInfo.displayPlatformType;
89 mPlatformParams.deviceType = traceInfo.displayDeviceType;
90 mPlatformParams.enable(angle::Feature::ForceInitShaderVariables);
91
92 if (!mEGLWindow->initializeGL(mOSWindow, mEntryPointsLib.get(),
93 angle::GLESDriverType::AngleEGL, mPlatformParams,
94 configParams))
95 {
96 mOSWindow->destroy();
97 return false;
98 }
99 // Disable vsync
100 if (!mEGLWindow->setSwapInterval(0))
101 {
102 cleanupTest();
103 return false;
104 }
105
106 // Load trace
107 mTraceLibrary.reset(new angle::TraceLibrary(traceInfo.name));
108 if (!mTraceLibrary->valid())
109 {
110 std::cout << "Failed to load trace library: " << traceInfo.name << "\n";
111 return false;
112 }
113
114 if (traceInfo.isBinaryDataCompressed)
115 {
116 mTraceLibrary->setBinaryDataDecompressCallback(angle::DecompressBinaryData);
117 }
118
119 std::stringstream binaryPathStream;
120 binaryPathStream << execDir << angle::GetPathSeparator()
121 << ANGLE_CAPTURE_REPLAY_TEST_DATA_DIR;
122
123 mTraceLibrary->setBinaryDataDir(binaryPathStream.str().c_str());
124
125 mTraceLibrary->setupReplay();
126 return true;
127 }
128
cleanupTest()129 void cleanupTest()
130 {
131 mTraceLibrary.reset(nullptr);
132 mEGLWindow->destroyGL();
133 mOSWindow->destroy();
134 }
135
swap()136 void swap() { mEGLWindow->swap(); }
137
runTest(const std::string & exeDir,const angle::TraceInfo & traceInfo)138 int runTest(const std::string &exeDir, const angle::TraceInfo &traceInfo)
139 {
140 if (!initializeTest(exeDir, traceInfo))
141 {
142 return -1;
143 }
144
145 for (uint32_t frame = traceInfo.frameStart; frame <= traceInfo.frameEnd; frame++)
146 {
147 mTraceLibrary->replayFrame(frame);
148
149 const char *replayedSerializedState =
150 reinterpret_cast<const char *>(glGetString(GL_SERIALIZED_CONTEXT_STRING_ANGLE));
151 const char *capturedSerializedState = mTraceLibrary->getSerializedContextState(frame);
152
153 bool isEqual =
154 (capturedSerializedState && replayedSerializedState)
155 ? compareSerializedContexts(replayedSerializedState, capturedSerializedState)
156 : (capturedSerializedState == replayedSerializedState);
157
158 // Swap always to allow RenderDoc/other tools to capture frames.
159 swap();
160 if (!isEqual)
161 {
162 std::ostringstream replayName;
163 replayName << exeDir << angle::GetPathSeparator() << traceInfo.name
164 << "_ContextReplayed" << frame << ".json";
165
166 std::ofstream debugReplay(replayName.str());
167 debugReplay << (replayedSerializedState ? replayedSerializedState : "") << "\n";
168
169 std::ostringstream captureName;
170 captureName << exeDir << angle::GetPathSeparator() << traceInfo.name
171 << "_ContextCaptured" << frame << ".json";
172 std::ofstream debugCapture(captureName.str());
173
174 debugCapture << (capturedSerializedState ? capturedSerializedState : "") << "\n";
175
176 cleanupTest();
177 return -1;
178 }
179 }
180 cleanupTest();
181 return 0;
182 }
183
run()184 int run()
185 {
186 std::string startingDirectory = angle::GetCWD().value();
187
188 // Set CWD to executable directory.
189 std::string exeDir = angle::GetExecutableDirectory();
190
191 std::vector<std::string> traces;
192
193 std::stringstream tracePathStream;
194 tracePathStream << exeDir << angle::GetPathSeparator() << kTracePath;
195
196 if (!angle::LoadTraceNamesFromJSON(tracePathStream.str(), &traces))
197 {
198 std::cout << "Unable to load trace names from " << kTracePath << "\n";
199 return 1;
200 }
201
202 for (const std::string &trace : traces)
203 {
204 std::stringstream traceJsonPathStream;
205 traceJsonPathStream << exeDir << angle::GetPathSeparator()
206 << ANGLE_CAPTURE_REPLAY_TEST_DATA_DIR << angle::GetPathSeparator()
207 << trace << ".json";
208 std::string traceJsonPath = traceJsonPathStream.str();
209
210 int result = -1;
211 angle::TraceInfo traceInfo = {};
212 if (!angle::LoadTraceInfoFromJSON(trace, traceJsonPath, &traceInfo))
213 {
214 std::cout << "Unable to load trace data: " << traceJsonPath << "\n";
215 }
216 else
217 {
218 result = runTest(exeDir, traceInfo);
219 }
220 std::cout << kResultTag << " " << trace << " " << result << "\n";
221 }
222
223 angle::SetCWD(startingDirectory.c_str());
224 return 0;
225 }
226
227 private:
compareSerializedContexts(const char * capturedSerializedContextState,const char * replaySerializedContextState)228 bool compareSerializedContexts(const char *capturedSerializedContextState,
229 const char *replaySerializedContextState)
230 {
231
232 return !strcmp(replaySerializedContextState, capturedSerializedContextState);
233 }
234
235 OSWindow *mOSWindow = nullptr;
236 EGLWindow *mEGLWindow = nullptr;
237 EGLPlatformParameters mPlatformParams;
238 // Handle to the entry point binding library.
239 std::unique_ptr<angle::Library> mEntryPointsLib;
240 std::unique_ptr<angle::TraceLibrary> mTraceLibrary;
241 };
242
main(int argc,char ** argv)243 int main(int argc, char **argv)
244 {
245 CaptureReplayTests app;
246 return app.run();
247 }
248