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 // TracePerf:
7 //   Performance test for ANGLE replaying traces.
8 //
9 
10 #include <gtest/gtest.h>
11 #include "common/PackedEnums.h"
12 #include "common/system_utils.h"
13 #include "tests/perf_tests/ANGLEPerfTest.h"
14 #include "tests/perf_tests/ANGLEPerfTestArgs.h"
15 #include "tests/perf_tests/DrawCallPerfParams.h"
16 #include "util/egl_loader_autogen.h"
17 #include "util/frame_capture_test_utils.h"
18 #include "util/png_utils.h"
19 
20 #include "restricted_traces/restricted_traces_autogen.h"
21 
22 #include <cassert>
23 #include <functional>
24 #include <sstream>
25 
26 using namespace angle;
27 using namespace egl_platform;
28 
29 namespace
30 {
31 struct TracePerfParams final : public RenderTestParams
32 {
33     // Common default options
TracePerfParams__anon74a605680111::TracePerfParams34     TracePerfParams()
35     {
36         majorVersion = 3;
37         minorVersion = 1;
38 
39         // Display the frame after every drawBenchmark invocation
40         iterationsPerStep = 1;
41     }
42 
story__anon74a605680111::TracePerfParams43     std::string story() const override
44     {
45         std::stringstream strstr;
46         strstr << RenderTestParams::story() << "_" << GetTraceInfo(testID).name;
47         return strstr.str();
48     }
49 
50     RestrictedTraceID testID;
51 };
52 
operator <<(std::ostream & os,const TracePerfParams & params)53 std::ostream &operator<<(std::ostream &os, const TracePerfParams &params)
54 {
55     os << params.backendAndStory().substr(1);
56     return os;
57 }
58 
59 class TracePerfTest : public ANGLERenderTest, public ::testing::WithParamInterface<TracePerfParams>
60 {
61   public:
62     TracePerfTest();
63 
64     void initializeBenchmark() override;
65     void destroyBenchmark() override;
66     void drawBenchmark() override;
67 
68     void onReplayFramebufferChange(GLenum target, GLuint framebuffer);
69     void onReplayInvalidateFramebuffer(GLenum target,
70                                        GLsizei numAttachments,
71                                        const GLenum *attachments);
72     void onReplayInvalidateSubFramebuffer(GLenum target,
73                                           GLsizei numAttachments,
74                                           const GLenum *attachments,
75                                           GLint x,
76                                           GLint y,
77                                           GLsizei width,
78                                           GLsizei height);
79     void onReplayDrawBuffers(GLsizei n, const GLenum *bufs);
80     void onReplayReadBuffer(GLenum src);
81     void onReplayDiscardFramebufferEXT(GLenum target,
82                                        GLsizei numAttachments,
83                                        const GLenum *attachments);
84 
85     bool isDefaultFramebuffer(GLenum target) const;
86 
87     uint32_t mStartFrame;
88     uint32_t mEndFrame;
89 
90     double getHostTimeFromGLTime(GLint64 glTime);
91 
92   private:
93     struct QueryInfo
94     {
95         GLuint beginTimestampQuery;
96         GLuint endTimestampQuery;
97         GLuint framebuffer;
98     };
99 
100     struct TimeSample
101     {
102         GLint64 glTime;
103         double hostTime;
104     };
105 
106     void sampleTime();
107     void saveScreenshot(const std::string &screenshotName) override;
108 
109     // For tracking RenderPass/FBO change timing.
110     QueryInfo mCurrentQuery = {};
111     std::vector<QueryInfo> mRunningQueries;
112     std::vector<TimeSample> mTimeline;
113 
114     std::string mStartingDirectory;
115     bool mUseTimestampQueries      = false;
116     GLuint mOffscreenFramebuffer   = 0;
117     GLuint mOffscreenTexture       = 0;
118     GLuint mOffscreenDepthStencil  = 0;
119     int mWindowWidth               = 0;
120     int mWindowHeight              = 0;
121     GLuint mDrawFramebufferBinding = 0;
122     GLuint mReadFramebufferBinding = 0;
123 };
124 
125 class TracePerfTest;
126 TracePerfTest *gCurrentTracePerfTest = nullptr;
127 
128 // Don't forget to include KHRONOS_APIENTRY in override methods. Neccessary on Win/x86.
BindFramebufferProc(GLenum target,GLuint framebuffer)129 void KHRONOS_APIENTRY BindFramebufferProc(GLenum target, GLuint framebuffer)
130 {
131     gCurrentTracePerfTest->onReplayFramebufferChange(target, framebuffer);
132 }
133 
InvalidateFramebufferProc(GLenum target,GLsizei numAttachments,const GLenum * attachments)134 void KHRONOS_APIENTRY InvalidateFramebufferProc(GLenum target,
135                                                 GLsizei numAttachments,
136                                                 const GLenum *attachments)
137 {
138     gCurrentTracePerfTest->onReplayInvalidateFramebuffer(target, numAttachments, attachments);
139 }
140 
InvalidateSubFramebufferProc(GLenum target,GLsizei numAttachments,const GLenum * attachments,GLint x,GLint y,GLsizei width,GLsizei height)141 void KHRONOS_APIENTRY InvalidateSubFramebufferProc(GLenum target,
142                                                    GLsizei numAttachments,
143                                                    const GLenum *attachments,
144                                                    GLint x,
145                                                    GLint y,
146                                                    GLsizei width,
147                                                    GLsizei height)
148 {
149     gCurrentTracePerfTest->onReplayInvalidateSubFramebuffer(target, numAttachments, attachments, x,
150                                                             y, width, height);
151 }
152 
DrawBuffersProc(GLsizei n,const GLenum * bufs)153 void KHRONOS_APIENTRY DrawBuffersProc(GLsizei n, const GLenum *bufs)
154 {
155     gCurrentTracePerfTest->onReplayDrawBuffers(n, bufs);
156 }
157 
ReadBufferProc(GLenum src)158 void KHRONOS_APIENTRY ReadBufferProc(GLenum src)
159 {
160     gCurrentTracePerfTest->onReplayReadBuffer(src);
161 }
162 
DiscardFramebufferEXTProc(GLenum target,GLsizei numAttachments,const GLenum * attachments)163 void KHRONOS_APIENTRY DiscardFramebufferEXTProc(GLenum target,
164                                                 GLsizei numAttachments,
165                                                 const GLenum *attachments)
166 {
167     gCurrentTracePerfTest->onReplayDiscardFramebufferEXT(target, numAttachments, attachments);
168 }
169 
TraceLoadProc(const char * procName)170 angle::GenericProc KHRONOS_APIENTRY TraceLoadProc(const char *procName)
171 {
172     if (strcmp(procName, "glBindFramebuffer") == 0)
173     {
174         return reinterpret_cast<angle::GenericProc>(BindFramebufferProc);
175     }
176     if (strcmp(procName, "glInvalidateFramebuffer") == 0)
177     {
178         return reinterpret_cast<angle::GenericProc>(InvalidateFramebufferProc);
179     }
180     if (strcmp(procName, "glInvalidateSubFramebuffer") == 0)
181     {
182         return reinterpret_cast<angle::GenericProc>(InvalidateSubFramebufferProc);
183     }
184     if (strcmp(procName, "glDrawBuffers") == 0)
185     {
186         return reinterpret_cast<angle::GenericProc>(DrawBuffersProc);
187     }
188     if (strcmp(procName, "glReadBuffer") == 0)
189     {
190         return reinterpret_cast<angle::GenericProc>(ReadBufferProc);
191     }
192     if (strcmp(procName, "glDiscardFramebufferEXT") == 0)
193     {
194         return reinterpret_cast<angle::GenericProc>(DiscardFramebufferEXTProc);
195     }
196     return gCurrentTracePerfTest->getGLWindow()->getProcAddress(procName);
197 }
198 
TracePerfTest()199 TracePerfTest::TracePerfTest()
200     : ANGLERenderTest("TracePerf", GetParam(), "ms"), mStartFrame(0), mEndFrame(0)
201 {
202     const TracePerfParams &param = GetParam();
203 
204     // TODO: http://anglebug.com/4533 This fails after the upgrade to the 26.20.100.7870 driver.
205     if (IsWindows() && IsIntel() && param.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE &&
206         param.testID == RestrictedTraceID::manhattan_10)
207     {
208         mSkipTest = true;
209     }
210 
211     // TODO: http://anglebug.com/4731 Fails on older Intel drivers. Passes in newer.
212     if (IsWindows() && IsIntel() && param.driver != GLESDriverType::AngleEGL &&
213         param.testID == RestrictedTraceID::angry_birds_2_1500)
214     {
215         mSkipTest = true;
216     }
217 
218     if (param.surfaceType != SurfaceType::Window && !gEnableAllTraceTests)
219     {
220         printf("Test skipped. Use --enable-all-trace-tests to run.\n");
221         mSkipTest = true;
222     }
223 
224     if (param.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE &&
225         !gEnableAllTraceTests)
226     {
227         printf("Test skipped. Use --enable-all-trace-tests to run.\n");
228         mSkipTest = true;
229     }
230 
231     if (param.testID == RestrictedTraceID::cod_mobile)
232     {
233         // TODO: http://anglebug.com/4967 Vulkan: GL_EXT_color_buffer_float not supported on Pixel 2
234         // The COD:Mobile trace uses a framebuffer attachment with:
235         //   format = GL_RGB
236         //   type = GL_UNSIGNED_INT_10F_11F_11F_REV
237         // That combination is only renderable if GL_EXT_color_buffer_float is supported.
238         // It happens to not be supported on Pixel 2's Vulkan driver.
239         addExtensionPrerequisite("GL_EXT_color_buffer_float");
240 
241         // TODO: http://anglebug.com/4731 This extension is missing on older Intel drivers.
242         addExtensionPrerequisite("GL_OES_EGL_image_external");
243     }
244 
245     if (param.testID == RestrictedTraceID::brawl_stars)
246     {
247         addExtensionPrerequisite("GL_EXT_shadow_samplers");
248     }
249 
250     if (param.testID == RestrictedTraceID::free_fire)
251     {
252         addExtensionPrerequisite("GL_OES_EGL_image_external");
253     }
254 
255     if (param.testID == RestrictedTraceID::marvel_contest_of_champions)
256     {
257         addExtensionPrerequisite("GL_EXT_color_buffer_half_float");
258     }
259 
260     if (param.testID == RestrictedTraceID::world_of_tanks_blitz)
261     {
262         addExtensionPrerequisite("GL_EXT_disjoint_timer_query");
263     }
264 
265     if (param.testID == RestrictedTraceID::dragon_ball_legends)
266     {
267         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
268     }
269 
270     // We already swap in TracePerfTest::drawBenchmark, no need to swap again in the harness.
271     disableTestHarnessSwap();
272 
273     gCurrentTracePerfTest = this;
274 }
275 
initializeBenchmark()276 void TracePerfTest::initializeBenchmark()
277 {
278     const auto &params = GetParam();
279 
280     mStartingDirectory = angle::GetCWD().value();
281 
282     // To load the trace data path correctly we set the CWD to the executable dir.
283     if (!IsAndroid())
284     {
285         std::string exeDir = angle::GetExecutableDirectory();
286         angle::SetCWD(exeDir.c_str());
287     }
288 
289     trace_angle::LoadGLES(TraceLoadProc);
290 
291     const TraceInfo &traceInfo = GetTraceInfo(params.testID);
292     mStartFrame                = traceInfo.startFrame;
293     mEndFrame                  = traceInfo.endFrame;
294     SetBinaryDataDecompressCallback(params.testID, DecompressBinaryData);
295 
296     setStepsPerRunLoopStep(mEndFrame - mStartFrame + 1);
297 
298     std::stringstream testDataDirStr;
299     testDataDirStr << ANGLE_TRACE_DATA_DIR << "/" << traceInfo.name;
300     std::string testDataDir = testDataDirStr.str();
301     SetBinaryDataDir(params.testID, testDataDir.c_str());
302 
303     mWindowWidth  = mTestParams.windowWidth;
304     mWindowHeight = mTestParams.windowHeight;
305 
306     if (IsAndroid())
307     {
308         // On Android, set the orientation used by the app, based on width/height
309         getWindow()->setOrientation(mTestParams.windowWidth, mTestParams.windowHeight);
310     }
311 
312     // If we're rendering offscreen we set up a default backbuffer.
313     if (params.surfaceType == SurfaceType::Offscreen)
314     {
315         if (!IsAndroid())
316         {
317             mWindowWidth *= 4;
318             mWindowHeight *= 4;
319         }
320 
321         glGenFramebuffers(1, &mOffscreenFramebuffer);
322         glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffer);
323 
324         // Hard-code RGBA8/D24S8. This should be specified in the trace info.
325         glGenTextures(1, &mOffscreenTexture);
326         glBindTexture(GL_TEXTURE_2D, mOffscreenTexture);
327         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWindowWidth, mWindowHeight, 0, GL_RGBA,
328                      GL_UNSIGNED_BYTE, nullptr);
329 
330         glGenRenderbuffers(1, &mOffscreenDepthStencil);
331         glBindRenderbuffer(GL_RENDERBUFFER, mOffscreenDepthStencil);
332         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mWindowWidth, mWindowHeight);
333 
334         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
335                                mOffscreenTexture, 0);
336         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
337                                   mOffscreenDepthStencil);
338         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
339                                   mOffscreenDepthStencil);
340         glBindTexture(GL_TEXTURE_2D, 0);
341         glBindRenderbuffer(GL_RENDERBUFFER, 0);
342     }
343 
344     // Potentially slow. Can load a lot of resources.
345     SetupReplay(params.testID);
346 
347     glFinish();
348 
349     ASSERT_TRUE(mEndFrame > mStartFrame);
350 
351     getWindow()->ignoreSizeEvents();
352     getWindow()->setVisible(true);
353 
354     // If we're re-tracing, trigger capture start after setup. This ensures the Setup function gets
355     // recaptured into another Setup function and not merged with the first frame.
356     if (angle::gStartTraceAfterSetup)
357     {
358         angle::SetEnvironmentVar("ANGLE_CAPTURE_TRIGGER", "0");
359         getGLWindow()->swap();
360     }
361 }
362 
363 #undef TRACE_TEST_CASE
364 
destroyBenchmark()365 void TracePerfTest::destroyBenchmark()
366 {
367     if (mOffscreenTexture != 0)
368     {
369         glDeleteTextures(1, &mOffscreenTexture);
370         mOffscreenTexture = 0;
371     }
372 
373     if (mOffscreenDepthStencil != 0)
374     {
375         glDeleteRenderbuffers(1, &mOffscreenDepthStencil);
376         mOffscreenDepthStencil = 0;
377     }
378 
379     if (mOffscreenFramebuffer != 0)
380     {
381         glDeleteFramebuffers(1, &mOffscreenFramebuffer);
382         mOffscreenFramebuffer = 0;
383     }
384 
385     // In order for the next test to load, restore the working directory
386     angle::SetCWD(mStartingDirectory.c_str());
387 }
388 
sampleTime()389 void TracePerfTest::sampleTime()
390 {
391     if (mUseTimestampQueries)
392     {
393         GLint64 glTime;
394         // glGetInteger64vEXT is exported by newer versions of the timer query extensions.
395         // Unfortunately only the core EP is exposed by some desktop drivers (e.g. NVIDIA).
396         if (glGetInteger64vEXT)
397         {
398             glGetInteger64vEXT(GL_TIMESTAMP_EXT, &glTime);
399         }
400         else
401         {
402             glGetInteger64v(GL_TIMESTAMP_EXT, &glTime);
403         }
404         mTimeline.push_back({glTime, angle::GetHostTimeSeconds()});
405     }
406 }
407 
drawBenchmark()408 void TracePerfTest::drawBenchmark()
409 {
410     constexpr uint32_t kFramesPerX  = 6;
411     constexpr uint32_t kFramesPerY  = 4;
412     constexpr uint32_t kFramesPerXY = kFramesPerY * kFramesPerX;
413 
414     const uint32_t kOffscreenOffsetX =
415         static_cast<uint32_t>(static_cast<double>(mTestParams.windowWidth) / 3.0f);
416     const uint32_t kOffscreenOffsetY =
417         static_cast<uint32_t>(static_cast<double>(mTestParams.windowHeight) / 3.0f);
418     const uint32_t kOffscreenWidth  = kOffscreenOffsetX;
419     const uint32_t kOffscreenHeight = kOffscreenOffsetY;
420 
421     const uint32_t kOffscreenFrameWidth = static_cast<uint32_t>(
422         static_cast<double>(kOffscreenWidth / static_cast<double>(kFramesPerX)));
423     const uint32_t kOffscreenFrameHeight = static_cast<uint32_t>(
424         static_cast<double>(kOffscreenHeight / static_cast<double>(kFramesPerY)));
425 
426     const TracePerfParams &params = GetParam();
427 
428     // Add a time sample from GL and the host.
429     sampleTime();
430 
431     uint32_t endFrame = mEndFrame;
432     if (gMaxStepsPerformed > 0)
433     {
434         endFrame =
435             std::min(endFrame, gMaxStepsPerformed - mTotalNumStepsPerformed - 1 + mStartFrame);
436         mStepsPerRunLoopStep = endFrame - mStartFrame + 1;
437     }
438 
439     for (uint32_t frame = mStartFrame; frame <= mEndFrame; ++frame)
440     {
441         char frameName[32];
442         sprintf(frameName, "Frame %u", frame);
443         beginInternalTraceEvent(frameName);
444 
445         startGpuTimer();
446         ReplayFrame(params.testID, frame);
447         stopGpuTimer();
448 
449         if (params.surfaceType != SurfaceType::Offscreen)
450         {
451             getGLWindow()->swap();
452         }
453         else
454         {
455             GLint currentDrawFBO, currentReadFBO;
456             glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &currentDrawFBO);
457             glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &currentReadFBO);
458 
459             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
460             glBindFramebuffer(GL_READ_FRAMEBUFFER, mOffscreenFramebuffer);
461 
462             uint32_t frames  = getNumStepsPerformed() + (frame - mStartFrame);
463             uint32_t frameX  = (frames % kFramesPerXY) % kFramesPerX;
464             uint32_t frameY  = (frames % kFramesPerXY) / kFramesPerX;
465             uint32_t windowX = kOffscreenOffsetX + frameX * kOffscreenFrameWidth;
466             uint32_t windowY = kOffscreenOffsetY + frameY * kOffscreenFrameHeight;
467 
468             if (angle::gVerboseLogging)
469             {
470                 printf("Frame %d: x %d y %d (screen x %d, screen y %d)\n", frames, frameX, frameY,
471                        windowX, windowY);
472             }
473 
474             GLboolean scissorTest = GL_FALSE;
475             glGetBooleanv(GL_SCISSOR_TEST, &scissorTest);
476 
477             if (scissorTest)
478             {
479                 glDisable(GL_SCISSOR_TEST);
480             }
481 
482             glBlitFramebuffer(0, 0, mWindowWidth, mWindowHeight, windowX, windowY,
483                               windowX + kOffscreenFrameWidth, windowY + kOffscreenFrameHeight,
484                               GL_COLOR_BUFFER_BIT, GL_NEAREST);
485 
486             if (frameX == kFramesPerX - 1 && frameY == kFramesPerY - 1)
487             {
488                 getGLWindow()->swap();
489                 glBindFramebuffer(GL_FRAMEBUFFER, 0);
490                 glClear(GL_COLOR_BUFFER_BIT);
491             }
492 
493             if (scissorTest)
494             {
495                 glEnable(GL_SCISSOR_TEST);
496             }
497             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentDrawFBO);
498             glBindFramebuffer(GL_READ_FRAMEBUFFER, currentReadFBO);
499         }
500 
501         endInternalTraceEvent(frameName);
502 
503         // Check for abnormal exit.
504         if (!mRunning)
505         {
506             return;
507         }
508     }
509 
510     ResetReplay(params.testID);
511 
512     // Process any running queries once per iteration.
513     for (size_t queryIndex = 0; queryIndex < mRunningQueries.size();)
514     {
515         const QueryInfo &query = mRunningQueries[queryIndex];
516 
517         GLuint endResultAvailable = 0;
518         glGetQueryObjectuivEXT(query.endTimestampQuery, GL_QUERY_RESULT_AVAILABLE,
519                                &endResultAvailable);
520 
521         if (endResultAvailable == GL_TRUE)
522         {
523             char fboName[32];
524             sprintf(fboName, "FBO %u", query.framebuffer);
525 
526             GLint64 beginTimestamp = 0;
527             glGetQueryObjecti64vEXT(query.beginTimestampQuery, GL_QUERY_RESULT, &beginTimestamp);
528             glDeleteQueriesEXT(1, &query.beginTimestampQuery);
529             double beginHostTime = getHostTimeFromGLTime(beginTimestamp);
530             beginGLTraceEvent(fboName, beginHostTime);
531 
532             GLint64 endTimestamp = 0;
533             glGetQueryObjecti64vEXT(query.endTimestampQuery, GL_QUERY_RESULT, &endTimestamp);
534             glDeleteQueriesEXT(1, &query.endTimestampQuery);
535             double endHostTime = getHostTimeFromGLTime(endTimestamp);
536             endGLTraceEvent(fboName, endHostTime);
537 
538             mRunningQueries.erase(mRunningQueries.begin() + queryIndex);
539         }
540         else
541         {
542             queryIndex++;
543         }
544     }
545 }
546 
547 // Converts a GL timestamp into a host-side CPU time aligned with "GetHostTimeSeconds".
548 // This check is necessary to line up sampled trace events in a consistent timeline.
549 // Uses a linear interpolation from a series of samples. We do a blocking call to sample
550 // both host and GL time once per swap. We then find the two closest GL timestamps and
551 // interpolate the host times between them to compute our result. If we are past the last
552 // GL timestamp we sample a new data point pair.
getHostTimeFromGLTime(GLint64 glTime)553 double TracePerfTest::getHostTimeFromGLTime(GLint64 glTime)
554 {
555     // Find two samples to do a lerp.
556     size_t firstSampleIndex = mTimeline.size() - 1;
557     while (firstSampleIndex > 0)
558     {
559         if (mTimeline[firstSampleIndex].glTime < glTime)
560         {
561             break;
562         }
563         firstSampleIndex--;
564     }
565 
566     // Add an extra sample if we're missing an ending sample.
567     if (firstSampleIndex == mTimeline.size() - 1)
568     {
569         sampleTime();
570     }
571 
572     const TimeSample &start = mTimeline[firstSampleIndex];
573     const TimeSample &end   = mTimeline[firstSampleIndex + 1];
574 
575     // Note: we have observed in some odd cases later timestamps producing values that are
576     // smaller than preceding timestamps. This bears further investigation.
577 
578     // Compute the scaling factor for the lerp.
579     double glDelta = static_cast<double>(glTime - start.glTime);
580     double glRange = static_cast<double>(end.glTime - start.glTime);
581     double t       = glDelta / glRange;
582 
583     // Lerp(t1, t2, t)
584     double hostRange = end.hostTime - start.hostTime;
585     return mTimeline[firstSampleIndex].hostTime + hostRange * t;
586 }
587 
588 // Triggered when the replay calls glBindFramebuffer.
onReplayFramebufferChange(GLenum target,GLuint framebuffer)589 void TracePerfTest::onReplayFramebufferChange(GLenum target, GLuint framebuffer)
590 {
591     if (framebuffer == 0 && GetParam().surfaceType == SurfaceType::Offscreen)
592     {
593         glBindFramebuffer(target, mOffscreenFramebuffer);
594     }
595     else
596     {
597         glBindFramebuffer(target, framebuffer);
598     }
599 
600     switch (target)
601     {
602         case GL_FRAMEBUFFER:
603             mDrawFramebufferBinding = framebuffer;
604             mReadFramebufferBinding = framebuffer;
605             break;
606         case GL_DRAW_FRAMEBUFFER:
607             mDrawFramebufferBinding = framebuffer;
608             break;
609         case GL_READ_FRAMEBUFFER:
610             mReadFramebufferBinding = framebuffer;
611             return;
612 
613         default:
614             UNREACHABLE();
615             break;
616     }
617 
618     if (!mUseTimestampQueries)
619         return;
620 
621     // We have at most one active timestamp query at a time. This code will end the current
622     // query and immediately start a new one.
623     if (mCurrentQuery.beginTimestampQuery != 0)
624     {
625         glGenQueriesEXT(1, &mCurrentQuery.endTimestampQuery);
626         glQueryCounterEXT(mCurrentQuery.endTimestampQuery, GL_TIMESTAMP_EXT);
627         mRunningQueries.push_back(mCurrentQuery);
628         mCurrentQuery = {};
629     }
630 
631     ASSERT(mCurrentQuery.beginTimestampQuery == 0);
632 
633     glGenQueriesEXT(1, &mCurrentQuery.beginTimestampQuery);
634     glQueryCounterEXT(mCurrentQuery.beginTimestampQuery, GL_TIMESTAMP_EXT);
635     mCurrentQuery.framebuffer = framebuffer;
636 }
637 
isDefaultFramebuffer(GLenum target) const638 bool TracePerfTest::isDefaultFramebuffer(GLenum target) const
639 {
640     switch (target)
641     {
642         case GL_FRAMEBUFFER:
643         case GL_DRAW_FRAMEBUFFER:
644             return (mDrawFramebufferBinding == 0);
645 
646         case GL_READ_FRAMEBUFFER:
647             return (mReadFramebufferBinding == 0);
648 
649         default:
650             UNREACHABLE();
651             return false;
652     }
653 }
654 
ConvertDefaultFramebufferEnum(GLenum value)655 GLenum ConvertDefaultFramebufferEnum(GLenum value)
656 {
657     switch (value)
658     {
659         case GL_NONE:
660             return GL_NONE;
661         case GL_BACK:
662         case GL_COLOR:
663             return GL_COLOR_ATTACHMENT0;
664         case GL_DEPTH:
665             return GL_DEPTH_ATTACHMENT;
666         case GL_STENCIL:
667             return GL_STENCIL_ATTACHMENT;
668         case GL_DEPTH_STENCIL:
669             return GL_DEPTH_STENCIL_ATTACHMENT;
670         default:
671             UNREACHABLE();
672             return GL_NONE;
673     }
674 }
675 
ConvertDefaultFramebufferEnums(GLsizei numAttachments,const GLenum * attachments)676 std::vector<GLenum> ConvertDefaultFramebufferEnums(GLsizei numAttachments,
677                                                    const GLenum *attachments)
678 {
679     std::vector<GLenum> translatedAttachments;
680     for (GLsizei attachmentIndex = 0; attachmentIndex < numAttachments; ++attachmentIndex)
681     {
682         GLenum converted = ConvertDefaultFramebufferEnum(attachments[attachmentIndex]);
683         translatedAttachments.push_back(converted);
684     }
685     return translatedAttachments;
686 }
687 
688 // Needs special handling to treat the 0 framebuffer in offscreen mode.
onReplayInvalidateFramebuffer(GLenum target,GLsizei numAttachments,const GLenum * attachments)689 void TracePerfTest::onReplayInvalidateFramebuffer(GLenum target,
690                                                   GLsizei numAttachments,
691                                                   const GLenum *attachments)
692 {
693     if (GetParam().surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
694     {
695         glInvalidateFramebuffer(target, numAttachments, attachments);
696     }
697     else
698     {
699         std::vector<GLenum> translatedAttachments =
700             ConvertDefaultFramebufferEnums(numAttachments, attachments);
701         glInvalidateFramebuffer(target, numAttachments, translatedAttachments.data());
702     }
703 }
704 
onReplayInvalidateSubFramebuffer(GLenum target,GLsizei numAttachments,const GLenum * attachments,GLint x,GLint y,GLsizei width,GLsizei height)705 void TracePerfTest::onReplayInvalidateSubFramebuffer(GLenum target,
706                                                      GLsizei numAttachments,
707                                                      const GLenum *attachments,
708                                                      GLint x,
709                                                      GLint y,
710                                                      GLsizei width,
711                                                      GLsizei height)
712 {
713     if (GetParam().surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
714     {
715         glInvalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height);
716     }
717     else
718     {
719         std::vector<GLenum> translatedAttachments =
720             ConvertDefaultFramebufferEnums(numAttachments, attachments);
721         glInvalidateSubFramebuffer(target, numAttachments, translatedAttachments.data(), x, y,
722                                    width, height);
723     }
724 }
725 
onReplayDrawBuffers(GLsizei n,const GLenum * bufs)726 void TracePerfTest::onReplayDrawBuffers(GLsizei n, const GLenum *bufs)
727 {
728     if (GetParam().surfaceType != SurfaceType::Offscreen ||
729         !isDefaultFramebuffer(GL_DRAW_FRAMEBUFFER))
730     {
731         glDrawBuffers(n, bufs);
732     }
733     else
734     {
735         std::vector<GLenum> translatedBufs = ConvertDefaultFramebufferEnums(n, bufs);
736         glDrawBuffers(n, translatedBufs.data());
737     }
738 }
739 
onReplayReadBuffer(GLenum src)740 void TracePerfTest::onReplayReadBuffer(GLenum src)
741 {
742     if (GetParam().surfaceType != SurfaceType::Offscreen ||
743         !isDefaultFramebuffer(GL_READ_FRAMEBUFFER))
744     {
745         glReadBuffer(src);
746     }
747     else
748     {
749         GLenum translated = ConvertDefaultFramebufferEnum(src);
750         glReadBuffer(translated);
751     }
752 }
753 
onReplayDiscardFramebufferEXT(GLenum target,GLsizei numAttachments,const GLenum * attachments)754 void TracePerfTest::onReplayDiscardFramebufferEXT(GLenum target,
755                                                   GLsizei numAttachments,
756                                                   const GLenum *attachments)
757 {
758     if (GetParam().surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
759     {
760         glDiscardFramebufferEXT(target, numAttachments, attachments);
761     }
762     else
763     {
764         std::vector<GLenum> translatedAttachments =
765             ConvertDefaultFramebufferEnums(numAttachments, attachments);
766         glDiscardFramebufferEXT(target, numAttachments, translatedAttachments.data());
767     }
768 }
769 
saveScreenshot(const std::string & screenshotName)770 void TracePerfTest::saveScreenshot(const std::string &screenshotName)
771 {
772     // Render a single frame.
773     RestrictedTraceID testID   = GetParam().testID;
774     const TraceInfo &traceInfo = GetTraceInfo(testID);
775     ReplayFrame(testID, traceInfo.startFrame);
776 
777     // RGBA 4-byte data.
778     uint32_t pixelCount = mTestParams.windowWidth * mTestParams.windowHeight;
779     std::vector<uint8_t> pixelData(pixelCount * 4);
780 
781     glBindFramebuffer(GL_FRAMEBUFFER, 0);
782     glReadPixels(0, 0, mTestParams.windowWidth, mTestParams.windowHeight, GL_RGBA, GL_UNSIGNED_BYTE,
783                  pixelData.data());
784 
785     // Convert to RGB and flip y.
786     std::vector<uint8_t> rgbData(pixelCount * 3);
787     for (EGLint y = 0; y < mTestParams.windowHeight; ++y)
788     {
789         for (EGLint x = 0; x < mTestParams.windowWidth; ++x)
790         {
791             EGLint srcPixel = x + y * mTestParams.windowWidth;
792             EGLint dstPixel = x + (mTestParams.windowHeight - y - 1) * mTestParams.windowWidth;
793             memcpy(&rgbData[dstPixel * 3], &pixelData[srcPixel * 4], 3);
794         }
795     }
796 
797     if (!angle::SavePNGRGB(screenshotName.c_str(), "ANGLE Screenshot", mTestParams.windowWidth,
798                            mTestParams.windowHeight, rgbData))
799     {
800         FAIL() << "Error saving screenshot: " << screenshotName;
801     }
802     else
803     {
804         printf("Saved screenshot: '%s'\n", screenshotName.c_str());
805     }
806 
807     // Finish the frame loop.
808     for (uint32_t nextFrame = traceInfo.startFrame + 1; nextFrame <= traceInfo.endFrame;
809          ++nextFrame)
810     {
811         ReplayFrame(testID, nextFrame);
812     }
813     ResetReplay(testID);
814     getGLWindow()->swap();
815     glFinish();
816 }
817 
TEST_P(TracePerfTest,Run)818 TEST_P(TracePerfTest, Run)
819 {
820     run();
821 }
822 
CombineTestID(const TracePerfParams & in,RestrictedTraceID id)823 TracePerfParams CombineTestID(const TracePerfParams &in, RestrictedTraceID id)
824 {
825     const TraceInfo &traceInfo = GetTraceInfo(id);
826 
827     TracePerfParams out = in;
828     out.testID          = id;
829     out.windowWidth     = traceInfo.drawSurfaceWidth;
830     out.windowHeight    = traceInfo.drawSurfaceHeight;
831     return out;
832 }
833 
NoAndroidMockICD(const TracePerfParams & in)834 bool NoAndroidMockICD(const TracePerfParams &in)
835 {
836     return in.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE || !IsAndroid();
837 }
838 
CombineWithSurfaceType(const TracePerfParams & in,SurfaceType surfaceType)839 TracePerfParams CombineWithSurfaceType(const TracePerfParams &in, SurfaceType surfaceType)
840 {
841     TracePerfParams out = in;
842     out.surfaceType     = surfaceType;
843 
844     if (!IsAndroid() && surfaceType == SurfaceType::Offscreen)
845     {
846         out.windowWidth /= 4;
847         out.windowHeight /= 4;
848     }
849 
850     // We track GPU time only in frame-rate-limited cases.
851     out.trackGpuTime = surfaceType == SurfaceType::WindowWithVSync;
852 
853     return out;
854 }
855 
856 using namespace params;
857 using P = TracePerfParams;
858 
859 std::vector<P> gTestsWithID =
860     CombineWithValues({P()}, AllEnums<RestrictedTraceID>(), CombineTestID);
861 std::vector<P> gTestsWithSurfaceType =
862     CombineWithValues(gTestsWithID,
863                       {SurfaceType::Offscreen, SurfaceType::Window, SurfaceType::WindowWithVSync},
864                       CombineWithSurfaceType);
865 std::vector<P> gTestsWithRenderer =
866     CombineWithFuncs(gTestsWithSurfaceType,
867                      {Vulkan<P>, VulkanMockICD<P>, VulkanSwiftShader<P>, Native<P>});
868 std::vector<P> gTestsWithoutMockICD = FilterWithFunc(gTestsWithRenderer, NoAndroidMockICD);
869 ANGLE_INSTANTIATE_TEST_ARRAY(TracePerfTest, gTestsWithoutMockICD);
870 
871 }  // anonymous namespace
872