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