xref: /aosp_15_r20/external/angle/src/tests/capture_replay_tests/CaptureReplayTests.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 "platform/PlatformMethods.h"
13 #include "traces_export.h"
14 #include "util/EGLPlatformParameters.h"
15 #include "util/EGLWindow.h"
16 #include "util/OSWindow.h"
17 #include "util/shader_utils.h"
18 #include "util/test_utils.h"
19 
20 #if defined(ANGLE_PLATFORM_ANDROID)
21 #    include "util/android/AndroidWindow.h"
22 #endif
23 
24 #include <stdint.h>
25 #include <string.h>
26 #include <fstream>
27 #include <functional>
28 #include <iostream>
29 #include <list>
30 #include <memory>
31 #include <ostream>
32 #include <string>
33 #include <utility>
34 
35 #include "frame_capture_test_utils.h"
36 
37 namespace
38 {
39 EGLWindow *gEGLWindow                = nullptr;
40 constexpr char kResultTag[]          = "*RESULT";
41 constexpr int kInitializationFailure = -1;
42 constexpr int kSerializationFailure  = -2;
43 constexpr int kExitSuccess           = 0;
44 
45 // This arbitrary value rejects placeholder serialized states. In practice they are many thousands
46 // of characters long. See frame_capture_utils_mock.cpp for the current placeholder string.
47 constexpr size_t kTooShortStateLength = 40;
48 
IsGLExtensionEnabled(const std::string & extName)49 [[maybe_unused]] bool IsGLExtensionEnabled(const std::string &extName)
50 {
51     return angle::CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
52                                        extName);
53 }
54 
DebugCallback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,const void * userParam)55 [[maybe_unused]] void KHRONOS_APIENTRY DebugCallback(GLenum source,
56                                                      GLenum type,
57                                                      GLuint id,
58                                                      GLenum severity,
59                                                      GLsizei length,
60                                                      const GLchar *message,
61                                                      const void *userParam)
62 {
63     printf("%s\n", message);
64 }
65 
LogError(angle::PlatformMethods * platform,const char * errorMessage)66 [[maybe_unused]] void LogError(angle::PlatformMethods *platform, const char *errorMessage)
67 {
68     printf("ERR: %s\n", errorMessage);
69 }
70 
LogWarning(angle::PlatformMethods * platform,const char * warningMessage)71 [[maybe_unused]] void LogWarning(angle::PlatformMethods *platform, const char *warningMessage)
72 {
73     printf("WARN: %s\n", warningMessage);
74 }
75 
LogInfo(angle::PlatformMethods * platform,const char * infoMessage)76 [[maybe_unused]] void LogInfo(angle::PlatformMethods *platform, const char *infoMessage)
77 {
78     printf("INFO: %s\n", infoMessage);
79 }
80 
CompareSerializedContexts(const char * capturedSerializedContextState,const char * replaySerializedContextState)81 bool CompareSerializedContexts(const char *capturedSerializedContextState,
82                                const char *replaySerializedContextState)
83 {
84 
85     return strcmp(replaySerializedContextState, capturedSerializedContextState) == 0;
86 }
87 
EGLCreateImage(EGLDisplay display,EGLContext context,EGLenum target,EGLClientBuffer buffer,const EGLAttrib * attrib_list)88 EGLImage KHRONOS_APIENTRY EGLCreateImage(EGLDisplay display,
89                                          EGLContext context,
90                                          EGLenum target,
91                                          EGLClientBuffer buffer,
92                                          const EGLAttrib *attrib_list)
93 {
94 
95     GLWindowContext ctx = reinterpret_cast<GLWindowContext>(context);
96     return gEGLWindow->createImage(ctx, target, buffer, attrib_list);
97 }
98 
EGLCreateImageKHR(EGLDisplay display,EGLContext context,EGLenum target,EGLClientBuffer buffer,const EGLint * attrib_list)99 EGLImage KHRONOS_APIENTRY EGLCreateImageKHR(EGLDisplay display,
100                                             EGLContext context,
101                                             EGLenum target,
102                                             EGLClientBuffer buffer,
103                                             const EGLint *attrib_list)
104 {
105 
106     GLWindowContext ctx = reinterpret_cast<GLWindowContext>(context);
107     return gEGLWindow->createImageKHR(ctx, target, buffer, attrib_list);
108 }
109 
EGLDestroyImage(EGLDisplay display,EGLImage image)110 EGLBoolean KHRONOS_APIENTRY EGLDestroyImage(EGLDisplay display, EGLImage image)
111 {
112     return gEGLWindow->destroyImage(image);
113 }
114 
EGLDestroyImageKHR(EGLDisplay display,EGLImage image)115 EGLBoolean KHRONOS_APIENTRY EGLDestroyImageKHR(EGLDisplay display, EGLImage image)
116 {
117     return gEGLWindow->destroyImageKHR(image);
118 }
119 
EGLCreatePbufferSurface(EGLDisplay display,EGLConfig * config,const EGLint * attrib_list)120 EGLSurface KHRONOS_APIENTRY EGLCreatePbufferSurface(EGLDisplay display,
121                                                     EGLConfig *config,
122                                                     const EGLint *attrib_list)
123 {
124     return gEGLWindow->createPbufferSurface(attrib_list);
125 }
126 
EGLDestroySurface(EGLDisplay display,EGLSurface surface)127 EGLBoolean KHRONOS_APIENTRY EGLDestroySurface(EGLDisplay display, EGLSurface surface)
128 {
129     return gEGLWindow->destroySurface(surface);
130 }
131 
EGLBindTexImage(EGLDisplay display,EGLSurface surface,EGLint buffer)132 EGLBoolean KHRONOS_APIENTRY EGLBindTexImage(EGLDisplay display, EGLSurface surface, EGLint buffer)
133 {
134     return gEGLWindow->bindTexImage(surface, buffer);
135 }
136 
EGLReleaseTexImage(EGLDisplay display,EGLSurface surface,EGLint buffer)137 EGLBoolean KHRONOS_APIENTRY EGLReleaseTexImage(EGLDisplay display,
138                                                EGLSurface surface,
139                                                EGLint buffer)
140 {
141     return gEGLWindow->releaseTexImage(surface, buffer);
142 }
143 
EGLMakeCurrent(EGLDisplay display,EGLSurface draw,EGLSurface read,EGLContext context)144 EGLBoolean KHRONOS_APIENTRY EGLMakeCurrent(EGLDisplay display,
145                                            EGLSurface draw,
146                                            EGLSurface read,
147                                            EGLContext context)
148 {
149     return gEGLWindow->makeCurrent(draw, read, context);
150 }
151 }  // namespace
152 
TraceLoadProc(const char * procName)153 GenericProc KHRONOS_APIENTRY TraceLoadProc(const char *procName)
154 {
155     if (!gEGLWindow)
156     {
157         std::cout << "No Window pointer in TraceLoadProc.\n";
158         return nullptr;
159     }
160     else
161     {
162         if (strcmp(procName, "eglCreateImage") == 0)
163         {
164             return reinterpret_cast<GenericProc>(EGLCreateImage);
165         }
166         if (strcmp(procName, "eglCreateImageKHR") == 0)
167         {
168             return reinterpret_cast<GenericProc>(EGLCreateImageKHR);
169         }
170         if (strcmp(procName, "eglDestroyImage") == 0)
171         {
172             return reinterpret_cast<GenericProc>(EGLDestroyImage);
173         }
174         if (strcmp(procName, "eglDestroyImageKHR") == 0)
175         {
176             return reinterpret_cast<GenericProc>(EGLDestroyImageKHR);
177         }
178         if (strcmp(procName, "eglCreatePbufferSurface") == 0)
179         {
180             return reinterpret_cast<GenericProc>(EGLCreatePbufferSurface);
181         }
182         if (strcmp(procName, "eglDestroySurface") == 0)
183         {
184             return reinterpret_cast<GenericProc>(EGLDestroySurface);
185         }
186         if (strcmp(procName, "eglBindTexImage") == 0)
187         {
188             return reinterpret_cast<GenericProc>(EGLBindTexImage);
189         }
190         if (strcmp(procName, "eglReleaseTexImage") == 0)
191         {
192             return reinterpret_cast<GenericProc>(EGLReleaseTexImage);
193         }
194         if (strcmp(procName, "eglMakeCurrent") == 0)
195         {
196             return reinterpret_cast<GenericProc>(EGLMakeCurrent);
197         }
198         return gEGLWindow->getProcAddress(procName);
199     }
200 }
201 
202 class CaptureReplayTests
203 {
204   public:
CaptureReplayTests()205     CaptureReplayTests()
206     {
207         // Load EGL library so we can initialize the display.
208         mEntryPointsLib.reset(
209             angle::OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, angle::SearchType::ModuleDir));
210 
211         mOSWindow = OSWindow::New();
212         mOSWindow->disableErrorMessageDialog();
213     }
214 
~CaptureReplayTests()215     ~CaptureReplayTests()
216     {
217         EGLWindow::Delete(&mEGLWindow);
218         OSWindow::Delete(&mOSWindow);
219     }
220 
initializeTest(const std::string & execDir,const angle::TraceInfo & traceInfo)221     bool initializeTest(const std::string &execDir, const angle::TraceInfo &traceInfo)
222     {
223         if (!mOSWindow->initialize(traceInfo.name, traceInfo.drawSurfaceWidth,
224                                    traceInfo.drawSurfaceHeight))
225         {
226             return false;
227         }
228 
229         mOSWindow->disableErrorMessageDialog();
230         mOSWindow->setVisible(true);
231 
232         if (mEGLWindow && !mEGLWindow->isContextVersion(traceInfo.contextClientMajorVersion,
233                                                         traceInfo.contextClientMinorVersion))
234         {
235             EGLWindow::Delete(&mEGLWindow);
236             mEGLWindow = nullptr;
237         }
238 
239         if (!mEGLWindow)
240         {
241             mEGLWindow = EGLWindow::New(traceInfo.contextClientMajorVersion,
242                                         traceInfo.contextClientMinorVersion);
243         }
244 
245         ConfigParameters configParams;
246         configParams.redBits     = traceInfo.configRedBits;
247         configParams.greenBits   = traceInfo.configGreenBits;
248         configParams.blueBits    = traceInfo.configBlueBits;
249         configParams.alphaBits   = traceInfo.configAlphaBits;
250         configParams.depthBits   = traceInfo.configDepthBits;
251         configParams.stencilBits = traceInfo.configStencilBits;
252 
253         configParams.clientArraysEnabled   = traceInfo.areClientArraysEnabled;
254         configParams.bindGeneratesResource = traceInfo.isBindGeneratesResourcesEnabled;
255         configParams.webGLCompatibility    = traceInfo.isWebGLCompatibilityEnabled;
256         configParams.robustResourceInit    = traceInfo.isRobustResourceInitEnabled;
257 
258         mPlatformParams.renderer   = traceInfo.displayPlatformType;
259         mPlatformParams.deviceType = traceInfo.displayDeviceType;
260         mPlatformParams.enable(angle::Feature::ForceInitShaderVariables);
261         mPlatformParams.enable(angle::Feature::EnableCaptureLimits);
262 
263 #if defined(ANGLE_ENABLE_ASSERTS)
264         mPlatformMethods.logError       = LogError;
265         mPlatformMethods.logWarning     = LogWarning;
266         mPlatformMethods.logInfo        = LogInfo;
267         mPlatformParams.platformMethods = &mPlatformMethods;
268 #endif  // defined(ANGLE_ENABLE_ASSERTS)
269 
270         if (!mEGLWindow->initializeGL(mOSWindow, mEntryPointsLib.get(),
271                                       angle::GLESDriverType::AngleEGL, mPlatformParams,
272                                       configParams))
273         {
274             mOSWindow->destroy();
275             return false;
276         }
277 
278         gEGLWindow = mEGLWindow;
279         LoadTraceEGL(TraceLoadProc);
280         LoadTraceGLES(TraceLoadProc);
281 
282         // Disable vsync
283         if (!mEGLWindow->setSwapInterval(0))
284         {
285             cleanupTest();
286             return false;
287         }
288 
289 #if defined(ANGLE_ENABLE_ASSERTS)
290         if (IsGLExtensionEnabled("GL_KHR_debug"))
291         {
292             EnableDebugCallback(DebugCallback, nullptr);
293         }
294 #endif
295 
296         std::string baseDir = "";
297 #if defined(ANGLE_TRACE_EXTERNAL_BINARIES)
298         baseDir += AndroidWindow::GetApplicationDirectory() + "/angle_traces/";
299 #endif
300         // Load trace
301         mTraceLibrary.reset(new angle::TraceLibrary(traceInfo.name, traceInfo, baseDir));
302         if (!mTraceLibrary->valid())
303         {
304             std::cout << "Failed to load trace library: " << traceInfo.name << "\n";
305             return false;
306         }
307 
308         std::stringstream binaryPathStream;
309         binaryPathStream << execDir << angle::GetPathSeparator()
310                          << ANGLE_CAPTURE_REPLAY_TEST_DATA_DIR;
311 
312         mTraceLibrary->setBinaryDataDir(binaryPathStream.str().c_str());
313 
314         mTraceLibrary->setupReplay();
315         return true;
316     }
317 
cleanupTest()318     void cleanupTest()
319     {
320         mTraceLibrary->finishReplay();
321         mTraceLibrary.reset(nullptr);
322         mEGLWindow->destroyGL();
323         mOSWindow->destroy();
324     }
325 
swap()326     void swap() { mEGLWindow->swap(); }
327 
runTest(const std::string & exeDir,const angle::TraceInfo & traceInfo)328     int runTest(const std::string &exeDir, const angle::TraceInfo &traceInfo)
329     {
330         if (!initializeTest(exeDir, traceInfo))
331         {
332             return kInitializationFailure;
333         }
334 
335         for (uint32_t frame = traceInfo.frameStart; frame <= traceInfo.frameEnd; frame++)
336         {
337             mTraceLibrary->replayFrame(frame);
338 
339             const char *replayedSerializedState =
340                 reinterpret_cast<const char *>(glGetString(GL_SERIALIZED_CONTEXT_STRING_ANGLE));
341             const char *capturedSerializedState = mTraceLibrary->getSerializedContextState(frame);
342 
343             if (replayedSerializedState == nullptr ||
344                 strlen(replayedSerializedState) <= kTooShortStateLength)
345             {
346                 printf("Could not retrieve replay serialized state string.\n");
347                 return kSerializationFailure;
348             }
349 
350             if (capturedSerializedState == nullptr ||
351                 strlen(capturedSerializedState) <= kTooShortStateLength)
352             {
353                 printf("Could not retrieve captured serialized state string.\n");
354                 return kSerializationFailure;
355             }
356 
357             // Swap always to allow RenderDoc/other tools to capture frames.
358             swap();
359             if (!CompareSerializedContexts(replayedSerializedState, capturedSerializedState))
360             {
361                 printf("Serialized contexts differ, saving files.\n");
362 
363                 std::ostringstream replayName;
364                 replayName << exeDir << angle::GetPathSeparator() << traceInfo.name
365                            << "_ContextReplayed" << frame << ".json";
366 
367                 std::ofstream debugReplay(replayName.str());
368                 if (!debugReplay)
369                 {
370                     printf("Error opening debug replay stream.\n");
371                 }
372                 else
373                 {
374                     debugReplay << (replayedSerializedState ? replayedSerializedState : "") << "\n";
375                     printf("Wrote %s.\n", replayName.str().c_str());
376                 }
377 
378                 std::ostringstream captureName;
379                 captureName << exeDir << angle::GetPathSeparator() << traceInfo.name
380                             << "_ContextCaptured" << frame << ".json";
381 
382                 std::ofstream debugCapture(captureName.str());
383                 if (!debugCapture)
384                 {
385                     printf("Error opening debug capture stream.\n");
386                 }
387                 else
388                 {
389                     debugCapture << (capturedSerializedState ? capturedSerializedState : "")
390                                  << "\n";
391                     printf("Wrote %s.\n", captureName.str().c_str());
392                 }
393 
394                 cleanupTest();
395                 return kSerializationFailure;
396             }
397         }
398         cleanupTest();
399         return kExitSuccess;
400     }
401 
run(const char * trace)402     int run(const char *trace)
403     {
404         std::string startingDirectory = angle::GetCWD().value();
405 
406         // Set CWD to executable directory.
407         std::string exeDir = angle::GetExecutableDirectory();
408 
409         std::stringstream traceJsonPathStream;
410         traceJsonPathStream << exeDir << angle::GetPathSeparator()
411                             << ANGLE_CAPTURE_REPLAY_TEST_DATA_DIR << angle::GetPathSeparator()
412                             << trace << ".json";
413         std::string traceJsonPath = traceJsonPathStream.str();
414 
415         int result                 = kInitializationFailure;
416         angle::TraceInfo traceInfo = {};
417         if (!angle::LoadTraceInfoFromJSON(trace, traceJsonPath, &traceInfo))
418         {
419             std::cout << "Unable to load trace data: " << traceJsonPath << "\n";
420         }
421         else
422         {
423             result = runTest(exeDir, traceInfo);
424         }
425         std::cout << kResultTag << " " << trace << " " << result << "\n";
426 
427         angle::SetCWD(startingDirectory.c_str());
428         return kExitSuccess;
429     }
430 
431   private:
432     OSWindow *mOSWindow   = nullptr;
433     EGLWindow *mEGLWindow = nullptr;
434     EGLPlatformParameters mPlatformParams;
435     angle::PlatformMethods mPlatformMethods;
436     // Handle to the entry point binding library.
437     std::unique_ptr<angle::Library> mEntryPointsLib;
438     std::unique_ptr<angle::TraceLibrary> mTraceLibrary;
439 };
440 
main(int argc,char ** argv)441 int main(int argc, char **argv)
442 {
443     if (argc != 2)
444     {
445         printf("Usage: capture_replay_tests {trace_label}\n");
446         return -1;
447     }
448     angle::CrashCallback crashCallback = []() {};
449     angle::InitCrashHandler(&crashCallback);
450 
451     CaptureReplayTests app;
452     return app.run(argv[1]);
453 }
454