Path: blob/main_old/src/tests/perf_tests/TracePerfTest.cpp
1693 views
//1// Copyright 2020 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//5// TracePerf:6// Performance test for ANGLE replaying traces.7//89#include <gtest/gtest.h>10#include "common/PackedEnums.h"11#include "common/system_utils.h"12#include "tests/perf_tests/ANGLEPerfTest.h"13#include "tests/perf_tests/ANGLEPerfTestArgs.h"14#include "tests/perf_tests/DrawCallPerfParams.h"15#include "util/egl_loader_autogen.h"16#include "util/frame_capture_test_utils.h"17#include "util/png_utils.h"18#include "util/test_utils.h"1920#include "restricted_traces/restricted_traces_autogen.h"2122#include <cassert>23#include <functional>24#include <sstream>2526// When --minimize-gpu-work is specified, we want to reduce GPU work to minimum and lift up the CPU27// overhead to surface so that we can see how much CPU overhead each driver has for each app trace.28// On some driver(s) the bufferSubData/texSubImage calls end up dominating the frame time when the29// actual GPU work is minimized. Even reducing the texSubImage calls to only update 1x1 area is not30// enough. The driver may be implementing copy on write by cloning the entire texture to another31// memory storage for texSubImage call. While this information is also important for performance,32// they should be evaluated separately in real app usage scenario, or write stand alone tests for33// these. For the purpose of CPU overhead and avoid data copy to dominate the trace, I am using this34// flag to noop the texSubImage and bufferSubData call when --minimize-gpu-work is specified. Feel35// free to disable this when you have other needs. Or it can be turned to another run time option36// when desired.37#define NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK3839using namespace angle;40using namespace egl_platform;4142namespace43{44struct TracePerfParams final : public RenderTestParams45{46// Common default options47TracePerfParams()48{49// Display the frame after every drawBenchmark invocation50iterationsPerStep = 1;51}5253std::string story() const override54{55std::stringstream strstr;56strstr << RenderTestParams::story() << "_" << GetTraceInfo(testID).name;57return strstr.str();58}5960RestrictedTraceID testID;61};6263std::ostream &operator<<(std::ostream &os, const TracePerfParams ¶ms)64{65os << params.backendAndStory().substr(1);66return os;67}6869class TracePerfTest : public ANGLERenderTest70{71public:72TracePerfTest(const TracePerfParams ¶ms);7374void initializeBenchmark() override;75void destroyBenchmark() override;76void drawBenchmark() override;7778// TODO(http://www.anglebug.com/5878): Add support for creating EGLSurface:79// - eglCreatePbufferSurface()80// - eglCreateWindowSurface()81EGLContext onEglCreateContext(EGLDisplay display,82EGLConfig config,83EGLContext share_context,84EGLint const *attrib_list);85void onEglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);86EGLContext onEglGetCurrentContext();87void onReplayFramebufferChange(GLenum target, GLuint framebuffer);88void onReplayInvalidateFramebuffer(GLenum target,89GLsizei numAttachments,90const GLenum *attachments);91void onReplayInvalidateSubFramebuffer(GLenum target,92GLsizei numAttachments,93const GLenum *attachments,94GLint x,95GLint y,96GLsizei width,97GLsizei height);98void onReplayDrawBuffers(GLsizei n, const GLenum *bufs);99void onReplayReadBuffer(GLenum src);100void onReplayDiscardFramebufferEXT(GLenum target,101GLsizei numAttachments,102const GLenum *attachments);103104void validateSerializedState(const char *serializedState, const char *fileName, uint32_t line);105106bool isDefaultFramebuffer(GLenum target) const;107108double getHostTimeFromGLTime(GLint64 glTime);109110int getStepAlignment() const override111{112// Align step counts to the number of frames in a trace.113const TraceInfo &traceInfo = GetTraceInfo(mParams.testID);114return static_cast<int>(traceInfo.endFrame - traceInfo.startFrame + 1);115}116117void TestBody() override { run(); }118119private:120struct QueryInfo121{122GLuint beginTimestampQuery;123GLuint endTimestampQuery;124GLuint framebuffer;125};126127struct TimeSample128{129GLint64 glTime;130double hostTime;131};132133void sampleTime();134void saveScreenshot(const std::string &screenshotName) override;135void swap();136137const TracePerfParams mParams;138139uint32_t mStartFrame;140uint32_t mEndFrame;141142// For tracking RenderPass/FBO change timing.143QueryInfo mCurrentQuery = {};144std::vector<QueryInfo> mRunningQueries;145std::vector<TimeSample> mTimeline;146147std::string mStartingDirectory;148bool mUseTimestampQueries = false;149static constexpr int mMaxOffscreenBufferCount = 2;150std::array<GLuint, mMaxOffscreenBufferCount> mOffscreenFramebuffers = {0, 0};151std::array<GLuint, mMaxOffscreenBufferCount> mOffscreenTextures = {0, 0};152GLuint mOffscreenDepthStencil = 0;153int mWindowWidth = 0;154int mWindowHeight = 0;155GLuint mDrawFramebufferBinding = 0;156GLuint mReadFramebufferBinding = 0;157uint32_t mCurrentFrame = 0;158uint32_t mOffscreenFrameCount = 0;159uint32_t mTotalFrameCount = 0;160bool mScreenshotSaved = false;161std::unique_ptr<TraceLibrary> mTraceLibrary;162};163164TracePerfTest *gCurrentTracePerfTest = nullptr;165166// Don't forget to include KHRONOS_APIENTRY in override methods. Necessary on Win/x86.167EGLContext KHRONOS_APIENTRY EglCreateContext(EGLDisplay display,168EGLConfig config,169EGLContext share_context,170EGLint const *attrib_list)171{172return gCurrentTracePerfTest->onEglCreateContext(display, config, share_context, attrib_list);173}174175void KHRONOS_APIENTRY EglMakeCurrent(EGLDisplay display,176EGLSurface draw,177EGLSurface read,178EGLContext context)179{180gCurrentTracePerfTest->onEglMakeCurrent(display, draw, read, context);181}182183EGLContext KHRONOS_APIENTRY EglGetCurrentContext()184{185return gCurrentTracePerfTest->onEglGetCurrentContext();186}187188void KHRONOS_APIENTRY BindFramebufferProc(GLenum target, GLuint framebuffer)189{190gCurrentTracePerfTest->onReplayFramebufferChange(target, framebuffer);191}192193void KHRONOS_APIENTRY InvalidateFramebufferProc(GLenum target,194GLsizei numAttachments,195const GLenum *attachments)196{197gCurrentTracePerfTest->onReplayInvalidateFramebuffer(target, numAttachments, attachments);198}199200void KHRONOS_APIENTRY InvalidateSubFramebufferProc(GLenum target,201GLsizei numAttachments,202const GLenum *attachments,203GLint x,204GLint y,205GLsizei width,206GLsizei height)207{208gCurrentTracePerfTest->onReplayInvalidateSubFramebuffer(target, numAttachments, attachments, x,209y, width, height);210}211212void KHRONOS_APIENTRY DrawBuffersProc(GLsizei n, const GLenum *bufs)213{214gCurrentTracePerfTest->onReplayDrawBuffers(n, bufs);215}216217void KHRONOS_APIENTRY ReadBufferProc(GLenum src)218{219gCurrentTracePerfTest->onReplayReadBuffer(src);220}221222void KHRONOS_APIENTRY DiscardFramebufferEXTProc(GLenum target,223GLsizei numAttachments,224const GLenum *attachments)225{226gCurrentTracePerfTest->onReplayDiscardFramebufferEXT(target, numAttachments, attachments);227}228229void KHRONOS_APIENTRY ViewportMinimizedProc(GLint x, GLint y, GLsizei width, GLsizei height)230{231glViewport(x, y, 1, 1);232}233234void KHRONOS_APIENTRY ScissorMinimizedProc(GLint x, GLint y, GLsizei width, GLsizei height)235{236glScissor(x, y, 1, 1);237}238239// Interpose the calls that generate actual GPU work240void KHRONOS_APIENTRY DrawElementsMinimizedProc(GLenum mode,241GLsizei count,242GLenum type,243const void *indices)244{245glDrawElements(GL_POINTS, 1, type, indices);246}247248void KHRONOS_APIENTRY DrawElementsIndirectMinimizedProc(GLenum mode,249GLenum type,250const void *indirect)251{252glDrawElementsInstancedBaseVertex(GL_POINTS, 1, type, 0, 1, 0);253}254255void KHRONOS_APIENTRY DrawElementsInstancedMinimizedProc(GLenum mode,256GLsizei count,257GLenum type,258const void *indices,259GLsizei instancecount)260{261glDrawElementsInstanced(GL_POINTS, 1, type, indices, 1);262}263264void KHRONOS_APIENTRY DrawElementsBaseVertexMinimizedProc(GLenum mode,265GLsizei count,266GLenum type,267const void *indices,268GLint basevertex)269{270glDrawElementsBaseVertex(GL_POINTS, 1, type, indices, basevertex);271}272273void KHRONOS_APIENTRY DrawElementsInstancedBaseVertexMinimizedProc(GLenum mode,274GLsizei count,275GLenum type,276const void *indices,277GLsizei instancecount,278GLint basevertex)279{280glDrawElementsInstancedBaseVertex(GL_POINTS, 1, type, indices, 1, basevertex);281}282283void KHRONOS_APIENTRY DrawRangeElementsMinimizedProc(GLenum mode,284GLuint start,285GLuint end,286GLsizei count,287GLenum type,288const void *indices)289{290glDrawRangeElements(GL_POINTS, start, end, 1, type, indices);291}292293void KHRONOS_APIENTRY DrawArraysMinimizedProc(GLenum mode, GLint first, GLsizei count)294{295glDrawArrays(GL_POINTS, first, 1);296}297298void KHRONOS_APIENTRY DrawArraysInstancedMinimizedProc(GLenum mode,299GLint first,300GLsizei count,301GLsizei instancecount)302{303glDrawArraysInstanced(GL_POINTS, first, 1, 1);304}305306void KHRONOS_APIENTRY DrawArraysIndirectMinimizedProc(GLenum mode, const void *indirect)307{308glDrawArraysInstanced(GL_POINTS, 0, 1, 1);309}310311void KHRONOS_APIENTRY DispatchComputeMinimizedProc(GLuint num_groups_x,312GLuint num_groups_y,313GLuint num_groups_z)314{315glDispatchCompute(1, 1, 1);316}317318void KHRONOS_APIENTRY DispatchComputeIndirectMinimizedProc(GLintptr indirect)319{320glDispatchCompute(1, 1, 1);321}322323// Interpose the calls that generate data copying work324void KHRONOS_APIENTRY BufferDataMinimizedProc(GLenum target,325GLsizeiptr size,326const void *data,327GLenum usage)328{329glBufferData(target, size, nullptr, usage);330}331332void KHRONOS_APIENTRY BufferSubDataMinimizedProc(GLenum target,333GLintptr offset,334GLsizeiptr size,335const void *data)336{337#if !defined(NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK)338glBufferSubData(target, offset, 1, data);339#endif340}341342void *KHRONOS_APIENTRY MapBufferRangeMinimizedProc(GLenum target,343GLintptr offset,344GLsizeiptr length,345GLbitfield access)346{347access |= GL_MAP_UNSYNCHRONIZED_BIT;348return glMapBufferRange(target, offset, length, access);349}350351void KHRONOS_APIENTRY TexImage2DMinimizedProc(GLenum target,352GLint level,353GLint internalformat,354GLsizei width,355GLsizei height,356GLint border,357GLenum format,358GLenum type,359const void *pixels)360{361GLint unpackBuffer = 0;362glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &unpackBuffer);363if (unpackBuffer)364{365glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);366}367glTexImage2D(target, level, internalformat, width, height, border, format, type, nullptr);368if (unpackBuffer)369{370glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBuffer);371}372}373374void KHRONOS_APIENTRY TexSubImage2DMinimizedProc(GLenum target,375GLint level,376GLint xoffset,377GLint yoffset,378GLsizei width,379GLsizei height,380GLenum format,381GLenum type,382const void *pixels)383{384#if !defined(NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK)385glTexSubImage2D(target, level, xoffset, yoffset, 1, 1, format, type, pixels);386#endif387}388389void KHRONOS_APIENTRY TexImage3DMinimizedProc(GLenum target,390GLint level,391GLint internalformat,392GLsizei width,393GLsizei height,394GLsizei depth,395GLint border,396GLenum format,397GLenum type,398const void *pixels)399{400GLint unpackBuffer = 0;401glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &unpackBuffer);402if (unpackBuffer)403{404glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);405}406glTexImage3D(target, level, internalformat, width, height, depth, border, format, type,407nullptr);408if (unpackBuffer)409{410glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBuffer);411}412}413414void KHRONOS_APIENTRY TexSubImage3DMinimizedProc(GLenum target,415GLint level,416GLint xoffset,417GLint yoffset,418GLint zoffset,419GLsizei width,420GLsizei height,421GLsizei depth,422GLenum format,423GLenum type,424const void *pixels)425{426#if !defined(NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK)427glTexSubImage3D(target, level, xoffset, yoffset, zoffset, 1, 1, 1, format, type, pixels);428#endif429}430431void KHRONOS_APIENTRY GenerateMipmapMinimizedProc(GLenum target)432{433// Noop it for now. There is a risk that this will leave an incomplete mipmap chain and cause434// other issues. If this turns out to be a real issue with app traces, we can turn this into a435// glTexImage2D call for each generated level.436}437438void KHRONOS_APIENTRY BlitFramebufferMinimizedProc(GLint srcX0,439GLint srcY0,440GLint srcX1,441GLint srcY1,442GLint dstX0,443GLint dstY0,444GLint dstX1,445GLint dstY1,446GLbitfield mask,447GLenum filter)448{449glBlitFramebuffer(srcX0, srcY0, srcX0 + 1, srcY0 + 1, dstX0, dstY0, dstX0 + 1, dstY0 + 1, mask,450filter);451}452453void KHRONOS_APIENTRY ReadPixelsMinimizedProc(GLint x,454GLint y,455GLsizei width,456GLsizei height,457GLenum format,458GLenum type,459void *pixels)460{461glReadPixels(x, y, 1, 1, format, type, pixels);462}463464void KHRONOS_APIENTRY BeginTransformFeedbackMinimizedProc(GLenum primitiveMode)465{466glBeginTransformFeedback(GL_POINTS);467}468469angle::GenericProc KHRONOS_APIENTRY TraceLoadProc(const char *procName)470{471// EGL472if (strcmp(procName, "eglCreateContext") == 0)473{474return reinterpret_cast<angle::GenericProc>(EglCreateContext);475}476if (strcmp(procName, "eglMakeCurrent") == 0)477{478return reinterpret_cast<angle::GenericProc>(EglMakeCurrent);479}480if (strcmp(procName, "eglGetCurrentContext") == 0)481{482return reinterpret_cast<angle::GenericProc>(EglGetCurrentContext);483}484485// GLES486if (strcmp(procName, "glBindFramebuffer") == 0)487{488return reinterpret_cast<angle::GenericProc>(BindFramebufferProc);489}490if (strcmp(procName, "glInvalidateFramebuffer") == 0)491{492return reinterpret_cast<angle::GenericProc>(InvalidateFramebufferProc);493}494if (strcmp(procName, "glInvalidateSubFramebuffer") == 0)495{496return reinterpret_cast<angle::GenericProc>(InvalidateSubFramebufferProc);497}498if (strcmp(procName, "glDrawBuffers") == 0)499{500return reinterpret_cast<angle::GenericProc>(DrawBuffersProc);501}502if (strcmp(procName, "glReadBuffer") == 0)503{504return reinterpret_cast<angle::GenericProc>(ReadBufferProc);505}506if (strcmp(procName, "glDiscardFramebufferEXT") == 0)507{508return reinterpret_cast<angle::GenericProc>(DiscardFramebufferEXTProc);509}510511if (gMinimizeGPUWork)512{513if (strcmp(procName, "glViewport") == 0)514{515return reinterpret_cast<angle::GenericProc>(ViewportMinimizedProc);516}517518if (strcmp(procName, "glScissor") == 0)519{520return reinterpret_cast<angle::GenericProc>(ScissorMinimizedProc);521}522523// Interpose the calls that generate actual GPU work524if (strcmp(procName, "glDrawElements") == 0)525{526return reinterpret_cast<angle::GenericProc>(DrawElementsMinimizedProc);527}528if (strcmp(procName, "glDrawElementsIndirect") == 0)529{530return reinterpret_cast<angle::GenericProc>(DrawElementsIndirectMinimizedProc);531}532if (strcmp(procName, "glDrawElementsInstanced") == 0 ||533strcmp(procName, "glDrawElementsInstancedEXT") == 0)534{535return reinterpret_cast<angle::GenericProc>(DrawElementsInstancedMinimizedProc);536}537if (strcmp(procName, "glDrawElementsBaseVertex") == 0 ||538strcmp(procName, "glDrawElementsBaseVertexEXT") == 0 ||539strcmp(procName, "glDrawElementsBaseVertexOES") == 0)540{541return reinterpret_cast<angle::GenericProc>(DrawElementsBaseVertexMinimizedProc);542}543if (strcmp(procName, "glDrawElementsInstancedBaseVertex") == 0 ||544strcmp(procName, "glDrawElementsInstancedBaseVertexEXT") == 0 ||545strcmp(procName, "glDrawElementsInstancedBaseVertexOES") == 0)546{547return reinterpret_cast<angle::GenericProc>(548DrawElementsInstancedBaseVertexMinimizedProc);549}550if (strcmp(procName, "glDrawRangeElements") == 0)551{552return reinterpret_cast<angle::GenericProc>(DrawRangeElementsMinimizedProc);553}554if (strcmp(procName, "glDrawArrays") == 0)555{556return reinterpret_cast<angle::GenericProc>(DrawArraysMinimizedProc);557}558if (strcmp(procName, "glDrawArraysInstanced") == 0 ||559strcmp(procName, "glDrawArraysInstancedEXT") == 0)560{561return reinterpret_cast<angle::GenericProc>(DrawArraysInstancedMinimizedProc);562}563if (strcmp(procName, "glDrawArraysIndirect") == 0)564{565return reinterpret_cast<angle::GenericProc>(DrawArraysIndirectMinimizedProc);566}567if (strcmp(procName, "glDispatchCompute") == 0)568{569return reinterpret_cast<angle::GenericProc>(DispatchComputeMinimizedProc);570}571if (strcmp(procName, "glDispatchComputeIndirect") == 0)572{573return reinterpret_cast<angle::GenericProc>(DispatchComputeIndirectMinimizedProc);574}575576// Interpose the calls that generate data copying work577if (strcmp(procName, "glBufferData") == 0)578{579return reinterpret_cast<angle::GenericProc>(BufferDataMinimizedProc);580}581if (strcmp(procName, "glBufferSubData") == 0)582{583return reinterpret_cast<angle::GenericProc>(BufferSubDataMinimizedProc);584}585if (strcmp(procName, "glMapBufferRange") == 0 ||586strcmp(procName, "glMapBufferRangeEXT") == 0)587{588return reinterpret_cast<angle::GenericProc>(MapBufferRangeMinimizedProc);589}590if (strcmp(procName, "glTexImage2D") == 0)591{592return reinterpret_cast<angle::GenericProc>(TexImage2DMinimizedProc);593}594if (strcmp(procName, "glTexImage3D") == 0)595{596return reinterpret_cast<angle::GenericProc>(TexImage3DMinimizedProc);597}598if (strcmp(procName, "glTexSubImage2D") == 0)599{600return reinterpret_cast<angle::GenericProc>(TexSubImage2DMinimizedProc);601}602if (strcmp(procName, "glTexSubImage3D") == 0)603{604return reinterpret_cast<angle::GenericProc>(TexSubImage3DMinimizedProc);605}606if (strcmp(procName, "glGenerateMipmap") == 0 ||607strcmp(procName, "glGenerateMipmapOES") == 0)608{609return reinterpret_cast<angle::GenericProc>(GenerateMipmapMinimizedProc);610}611if (strcmp(procName, "glBlitFramebuffer") == 0)612{613return reinterpret_cast<angle::GenericProc>(BlitFramebufferMinimizedProc);614}615if (strcmp(procName, "glReadPixels") == 0)616{617return reinterpret_cast<angle::GenericProc>(ReadPixelsMinimizedProc);618}619if (strcmp(procName, "glBeginTransformFeedback") == 0)620{621return reinterpret_cast<angle::GenericProc>(BeginTransformFeedbackMinimizedProc);622}623}624625return gCurrentTracePerfTest->getGLWindow()->getProcAddress(procName);626}627628void ValidateSerializedState(const char *serializedState, const char *fileName, uint32_t line)629{630gCurrentTracePerfTest->validateSerializedState(serializedState, fileName, line);631}632633TracePerfTest::TracePerfTest(const TracePerfParams ¶ms)634: ANGLERenderTest("TracePerf", params, "ms"), mParams(params), mStartFrame(0), mEndFrame(0)635{636// TODO: http://anglebug.com/4533 This fails after the upgrade to the 26.20.100.7870 driver.637if (IsWindows() && IsIntel() && mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE &&638mParams.testID == RestrictedTraceID::manhattan_10)639{640mSkipTest = true;641}642643// TODO: http://anglebug.com/4731 Fails on older Intel drivers. Passes in newer.644if (IsWindows() && IsIntel() && mParams.driver != GLESDriverType::AngleEGL &&645mParams.testID == RestrictedTraceID::angry_birds_2_1500)646{647mSkipTest = true;648}649650if (mParams.surfaceType != SurfaceType::Window && !gEnableAllTraceTests)651{652printf("Test skipped. Use --enable-all-trace-tests to run.\n");653mSkipTest = true;654}655656if (mParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE &&657!gEnableAllTraceTests)658{659printf("Test skipped. Use --enable-all-trace-tests to run.\n");660mSkipTest = true;661}662663if (mParams.testID == RestrictedTraceID::cod_mobile)664{665// TODO: http://anglebug.com/4967 Vulkan: GL_EXT_color_buffer_float not supported on Pixel 2666// The COD:Mobile trace uses a framebuffer attachment with:667// format = GL_RGB668// type = GL_UNSIGNED_INT_10F_11F_11F_REV669// That combination is only renderable if GL_EXT_color_buffer_float is supported.670// It happens to not be supported on Pixel 2's Vulkan driver.671addExtensionPrerequisite("GL_EXT_color_buffer_float");672673// TODO: http://anglebug.com/4731 This extension is missing on older Intel drivers.674addExtensionPrerequisite("GL_OES_EGL_image_external");675}676677if (mParams.testID == RestrictedTraceID::brawl_stars)678{679addExtensionPrerequisite("GL_EXT_shadow_samplers");680}681682if (mParams.testID == RestrictedTraceID::free_fire)683{684addExtensionPrerequisite("GL_OES_EGL_image_external");685}686687if (mParams.testID == RestrictedTraceID::marvel_contest_of_champions)688{689addExtensionPrerequisite("GL_EXT_color_buffer_half_float");690}691692if (mParams.testID == RestrictedTraceID::world_of_tanks_blitz)693{694addExtensionPrerequisite("GL_EXT_disjoint_timer_query");695}696697if (mParams.testID == RestrictedTraceID::dragon_ball_legends)698{699addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");700}701702if (mParams.testID == RestrictedTraceID::lego_legacy)703{704addExtensionPrerequisite("GL_EXT_shadow_samplers");705}706707if (mParams.testID == RestrictedTraceID::world_war_doh)708{709// Linux+Nvidia doesn't support GL_KHR_texture_compression_astc_ldr (possibly others also)710addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");711}712713if (mParams.testID == RestrictedTraceID::saint_seiya_awakening)714{715addExtensionPrerequisite("GL_EXT_shadow_samplers");716717// TODO(https://anglebug.com/5517) Linux+Intel generates "Framebuffer is incomplete" errors.718if (IsLinux() && IsIntel() && mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)719{720mSkipTest = true;721}722}723724if (mParams.testID == RestrictedTraceID::magic_tiles_3)725{726// Linux+Nvidia doesn't support GL_KHR_texture_compression_astc_ldr (possibly others also)727addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");728}729730if (mParams.testID == RestrictedTraceID::real_gangster_crime)731{732// Linux+Nvidia doesn't support GL_KHR_texture_compression_astc_ldr (possibly others also)733addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");734735// Intel doesn't support external images.736addExtensionPrerequisite("GL_OES_EGL_image_external");737738// Failing on Linux Intel and AMD due to invalid enum. http://anglebug.com/5822739if (IsLinux() && (IsIntel() || IsAMD()) && mParams.driver != GLESDriverType::AngleEGL)740{741mSkipTest = true;742}743}744745if (mParams.testID == RestrictedTraceID::asphalt_8)746{747addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");748}749750if (mParams.testID == RestrictedTraceID::hearthstone)751{752addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");753}754755if (mParams.testID == RestrictedTraceID::efootball_pes_2021)756{757// TODO(https://anglebug.com/5517) Linux+Intel and Pixel 2 generate "Framebuffer is758// incomplete" errors with the Vulkan backend.759if (mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE &&760((IsLinux() && IsIntel()) || IsPixel2()))761{762mSkipTest = true;763}764}765766if (mParams.testID == RestrictedTraceID::manhattan_31)767{768// TODO: http://anglebug.com/5591 Trace crashes on Pixel 2 in vulkan driver769if (IsPixel2() && mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)770{771mSkipTest = true;772}773}774775if (mParams.testID == RestrictedTraceID::idle_heroes)776{777// TODO: http://anglebug.com/5591 Trace crashes on Pixel 2778if (IsPixel2())779{780mSkipTest = true;781}782}783784if (mParams.testID == RestrictedTraceID::shadow_fight_2)785{786addExtensionPrerequisite("GL_OES_EGL_image_external");787addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");788}789790if (mParams.testID == RestrictedTraceID::rise_of_kingdoms)791{792addExtensionPrerequisite("GL_OES_EGL_image_external");793}794795if (mParams.testID == RestrictedTraceID::happy_color)796{797if (IsWindows() && IsAMD() && mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)798{799mSkipTest = true;800}801}802803if (mParams.testID == RestrictedTraceID::bus_simulator_indonesia)804{805// TODO(https://anglebug.com/5629) Linux+(Intel|AMD) native GLES generates806// GL_INVALID_OPERATION807if (IsLinux() && (IsIntel() || IsAMD()) &&808mParams.getRenderer() != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)809{810mSkipTest = true;811}812}813814if (mParams.testID == RestrictedTraceID::messenger_lite)815{816// TODO: https://anglebug.com/5663 Incorrect pixels on Nvidia Windows for first frame817if (IsWindows() && IsNVIDIA() &&818mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE &&819mParams.getDeviceType() != EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE)820{821mSkipTest = true;822}823}824825if (mParams.testID == RestrictedTraceID::among_us)826{827addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");828}829830if (mParams.testID == RestrictedTraceID::car_parking_multiplayer)831{832// TODO: https://anglebug.com/5613 Nvidia native driver spews undefined behavior warnings833if (IsNVIDIA() && mParams.getRenderer() != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)834{835mSkipTest = true;836}837// TODO: https://anglebug.com/5724 Device lost on Win Intel838if (IsWindows() && IsIntel() &&839mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)840{841mSkipTest = true;842}843}844845if (mParams.testID == RestrictedTraceID::fifa_mobile)846{847// TODO: http://anglebug.com/5875 Intel Windows Vulkan flakily renders entirely black848if (IsWindows() && IsIntel() &&849mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)850{851mSkipTest = true;852}853}854855if (mParams.testID == RestrictedTraceID::rope_hero_vice_town)856{857// TODO: http://anglebug.com/5716 Trace crashes on Pixel 2 in vulkan driver858if (IsPixel2() && mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)859{860mSkipTest = true;861}862}863864if (mParams.testID == RestrictedTraceID::extreme_car_driving_simulator)865{866addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");867}868869if (mParams.testID == RestrictedTraceID::plants_vs_zombies_2)870{871// TODO: http://crbug.com/1187752 Corrupted image872if (IsWindows() && IsAMD() && mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)873{874mSkipTest = true;875}876}877878if (mParams.testID == RestrictedTraceID::junes_journey)879{880addExtensionPrerequisite("GL_OES_EGL_image_external");881}882883if (mParams.testID == RestrictedTraceID::ragnarok_m_eternal_love)884{885addExtensionPrerequisite("GL_OES_EGL_image_external");886addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");887888// TODO: http://anglebug.com/5772 Pixel 2 errors with "Framebuffer is incomplete" on Vulkan889if (IsPixel2() && mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)890{891mSkipTest = true;892}893}894895if (mParams.testID == RestrictedTraceID::real_cricket_20)896{897// TODO: http://anglebug.com/5777 ARM doesn't have enough VS storage blocks898if (IsAndroid() && IsARM())899{900mSkipTest = true;901}902}903904if (mParams.testID == RestrictedTraceID::league_of_legends_wild_rift)905{906addExtensionPrerequisite("GL_OES_EGL_image_external");907addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");908909// TODO: http://anglebug.com/5815 Trace is crashing on Intel Linux910if (IsLinux() && IsIntel() && mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)911{912mSkipTest = true;913}914}915916if (mParams.testID == RestrictedTraceID::aztec_ruins)917{918addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");919920// TODO: http://anglebug.com/5553 Pixel 2 errors with "Framebuffer is incomplete" on Vulkan921if (IsPixel2() && mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)922{923mSkipTest = true;924}925}926927if (mParams.testID == RestrictedTraceID::dragon_raja)928{929addExtensionPrerequisite("GL_OES_EGL_image_external");930931// TODO: http://anglebug.com/5807 Intel Linux and Pixel 2 error with "Framebuffer is932// incomplete" on Vulkan933if (((IsLinux() && IsIntel()) || IsPixel2()) &&934mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)935{936mSkipTest = true;937}938}939940// Adreno gives a driver error with empty/small draw calls. http://anglebug.com/5823941if (mParams.testID == RestrictedTraceID::hill_climb_racing)942{943if (IsAndroid() && (IsPixel2() || IsPixel4()) &&944mParams.driver == GLESDriverType::SystemEGL)945{946mSkipTest = true;947}948}949950if (mParams.testID == RestrictedTraceID::avakin_life)951{952addExtensionPrerequisite("GL_OES_EGL_image_external");953}954955if (mParams.testID == RestrictedTraceID::professional_baseball_spirits)956{957// TODO(https://anglebug.com/5827) Linux+Mesa/RADV Vulkan generates958// GL_INVALID_FRAMEBUFFER_OPERATION.959// Mesa versions below 20.3.5 produce the same issue on Linux+Mesa/Intel Vulkan960if (IsLinux() && (IsAMD() || IsIntel()) &&961mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE &&962mParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE)963{964mSkipTest = true;965}966}967968if (mParams.testID == RestrictedTraceID::call_break_offline_card_game)969{970// TODO: http://anglebug.com/5837 Intel Linux Vulkan errors with "Framebuffer is incomplete"971if ((IsLinux() && IsIntel()) &&972mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)973{974mSkipTest = true;975}976}977978if (mParams.testID == RestrictedTraceID::slingshot_test1 ||979mParams.testID == RestrictedTraceID::slingshot_test2)980{981// TODO: http://anglebug.com/5877 Trace crashes on Pixel 2 in vulkan driver982if (IsPixel2() && mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)983{984mSkipTest = true;985}986}987988if (mParams.testID == RestrictedTraceID::ludo_king)989{990addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");991}992993// TODO: http://anglebug.com/5943 GL_INVALID_ENUM on Windows/Intel.994if (mParams.testID == RestrictedTraceID::summoners_war)995{996if (IsWindows() && IsIntel() && mParams.driver != GLESDriverType::AngleEGL)997{998mSkipTest = true;999}1000}10011002if (mParams.testID == RestrictedTraceID::pokemon_go)1003{1004addExtensionPrerequisite("GL_EXT_texture_cube_map_array");1005addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");10061007// TODO: http://anglebug.com/5989 Intel Linux crashing on teardown1008// TODO: http://anglebug.com/5994 Intel Windows timing out periodically1009if ((IsLinux() || IsWindows()) && IsIntel() &&1010mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)1011{1012mSkipTest = true;1013}1014}10151016if (mParams.testID == RestrictedTraceID::cookie_run_kingdom)1017{1018addExtensionPrerequisite("GL_EXT_texture_cube_map_array");1019addExtensionPrerequisite("GL_OES_EGL_image_external");10201021// TODO: http://anglebug.com/6017 ARM doesn't have enough VS storage blocks1022if (IsAndroid() && IsARM())1023{1024mSkipTest = true;1025}1026}10271028if (mParams.testID == RestrictedTraceID::genshin_impact)1029{1030addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");10311032// TODO: http://anglebug.com/6023 Crashes on Pixel 2 in vulkan driver1033// TODO: http://anglebug.com/6029 Crashes on Linux Intel Vulkan1034if (((IsLinux() && IsIntel()) || IsPixel2()) &&1035mParams.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE)1036{1037mSkipTest = true;1038}1039}10401041if (mParams.testID == RestrictedTraceID::pubg_mobile_skydive ||1042mParams.testID == RestrictedTraceID::pubg_mobile_battle_royale)1043{1044addExtensionPrerequisite("GL_EXT_texture_buffer");10451046// TODO: http://anglebug.com/6240 Internal errors on Windows using Intel or Nvida1047if (IsWindows() && (IsIntel() || IsNVIDIA()) && mParams.driver == GLESDriverType::SystemWGL)1048{1049mSkipTest = true;1050}1051}10521053if (mParams.testID == RestrictedTraceID::sakura_school_simulator)1054{1055// Flaky on Intel. http://anglebug.com/62941056if (IsWindows() && IsIntel())1057{1058mSkipTest = true;1059}1060}10611062if (mParams.testID == RestrictedTraceID::scrabble_go)1063{1064addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");1065}10661067if (mParams.testID == RestrictedTraceID::world_of_kings)1068{1069addExtensionPrerequisite("GL_OES_EGL_image_external");1070}10711072// We already swap in TracePerfTest::drawBenchmark, no need to swap again in the harness.1073disableTestHarnessSwap();10741075gCurrentTracePerfTest = this;10761077if (gTraceTestValidation)1078{1079const TraceInfo &traceInfo = GetTraceInfo(mParams.testID);1080mStepsToRun = (traceInfo.endFrame - traceInfo.startFrame + 1);1081}1082}10831084void TracePerfTest::initializeBenchmark()1085{1086const TraceInfo &traceInfo = GetTraceInfo(mParams.testID);10871088mStartingDirectory = angle::GetCWD().value();10891090std::stringstream traceNameStr;1091traceNameStr << "angle_restricted_trace_" << traceInfo.name;1092std::string traceName = traceNameStr.str();1093mTraceLibrary.reset(new TraceLibrary(traceName.c_str()));10941095// To load the trace data path correctly we set the CWD to the executable dir.1096if (!IsAndroid())1097{1098std::string exeDir = angle::GetExecutableDirectory();1099angle::SetCWD(exeDir.c_str());1100}11011102trace_angle::LoadEGL(TraceLoadProc);1103trace_angle::LoadGLES(TraceLoadProc);11041105if (!mTraceLibrary->valid())1106{1107ERR() << "Could not load trace library.";1108mSkipTest = true;1109return;1110}11111112mStartFrame = traceInfo.startFrame;1113mEndFrame = traceInfo.endFrame;1114mTraceLibrary->setBinaryDataDecompressCallback(DecompressBinaryData);11151116mTraceLibrary->setValidateSerializedStateCallback(ValidateSerializedState);11171118std::string relativeTestDataDir = std::string("src/tests/restricted_traces/") + traceInfo.name;11191120constexpr size_t kMaxDataDirLen = 1000;1121char testDataDir[kMaxDataDirLen];1122if (!angle::FindTestDataPath(relativeTestDataDir.c_str(), testDataDir, kMaxDataDirLen))1123{1124ERR() << "Could not find test data folder.";1125mSkipTest = true;1126return;1127}11281129mTraceLibrary->setBinaryDataDir(testDataDir);11301131if (gMinimizeGPUWork)1132{1133// Shrink the offscreen window to 1x1.1134mWindowWidth = 1;1135mWindowHeight = 1;1136}1137else1138{1139mWindowWidth = mTestParams.windowWidth;1140mWindowHeight = mTestParams.windowHeight;1141}1142mCurrentFrame = mStartFrame;11431144if (IsAndroid())1145{1146// On Android, set the orientation used by the app, based on width/height1147getWindow()->setOrientation(mTestParams.windowWidth, mTestParams.windowHeight);1148}11491150// If we're rendering offscreen we set up a default back buffer.1151if (mParams.surfaceType == SurfaceType::Offscreen)1152{1153if (!IsAndroid())1154{1155mWindowWidth *= 4;1156mWindowHeight *= 4;1157}11581159glGenRenderbuffers(1, &mOffscreenDepthStencil);1160glBindRenderbuffer(GL_RENDERBUFFER, mOffscreenDepthStencil);1161glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mWindowWidth, mWindowHeight);1162glBindRenderbuffer(GL_RENDERBUFFER, 0);11631164glGenFramebuffers(mMaxOffscreenBufferCount, mOffscreenFramebuffers.data());1165glGenTextures(mMaxOffscreenBufferCount, mOffscreenTextures.data());1166for (int i = 0; i < mMaxOffscreenBufferCount; i++)1167{1168glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffers[i]);11691170// Hard-code RGBA8/D24S8. This should be specified in the trace info.1171glBindTexture(GL_TEXTURE_2D, mOffscreenTextures[i]);1172glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWindowWidth, mWindowHeight, 0, GL_RGBA,1173GL_UNSIGNED_BYTE, nullptr);11741175glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,1176mOffscreenTextures[i], 0);1177glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,1178mOffscreenDepthStencil);1179glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,1180mOffscreenDepthStencil);1181glBindTexture(GL_TEXTURE_2D, 0);1182}1183}11841185// Potentially slow. Can load a lot of resources.1186mTraceLibrary->setupReplay();11871188glFinish();11891190ASSERT_GE(mEndFrame, mStartFrame);11911192getWindow()->ignoreSizeEvents();1193getWindow()->setVisible(true);11941195// If we're re-tracing, trigger capture start after setup. This ensures the Setup function gets1196// recaptured into another Setup function and not merged with the first frame.1197if (gRetraceMode)1198{1199getGLWindow()->swap();1200}1201}12021203#undef TRACE_TEST_CASE12041205void TracePerfTest::destroyBenchmark()1206{1207if (mParams.surfaceType == SurfaceType::Offscreen)1208{1209glDeleteTextures(mMaxOffscreenBufferCount, mOffscreenTextures.data());1210mOffscreenTextures.fill(0);12111212glDeleteRenderbuffers(1, &mOffscreenDepthStencil);1213mOffscreenDepthStencil = 0;12141215glDeleteFramebuffers(mMaxOffscreenBufferCount, mOffscreenFramebuffers.data());1216mOffscreenFramebuffers.fill(0);1217}12181219mTraceLibrary->finishReplay();1220mTraceLibrary.reset(nullptr);12211222// In order for the next test to load, restore the working directory1223angle::SetCWD(mStartingDirectory.c_str());1224}12251226void TracePerfTest::sampleTime()1227{1228if (mUseTimestampQueries)1229{1230GLint64 glTime;1231// glGetInteger64vEXT is exported by newer versions of the timer query extensions.1232// Unfortunately only the core EP is exposed by some desktop drivers (e.g. NVIDIA).1233if (glGetInteger64vEXT)1234{1235glGetInteger64vEXT(GL_TIMESTAMP_EXT, &glTime);1236}1237else1238{1239glGetInteger64v(GL_TIMESTAMP_EXT, &glTime);1240}1241mTimeline.push_back({glTime, angle::GetHostTimeSeconds()});1242}1243}12441245void TracePerfTest::drawBenchmark()1246{1247constexpr uint32_t kFramesPerX = 6;1248constexpr uint32_t kFramesPerY = 4;1249constexpr uint32_t kFramesPerXY = kFramesPerY * kFramesPerX;12501251const uint32_t kOffscreenOffsetX =1252static_cast<uint32_t>(static_cast<double>(mTestParams.windowWidth) / 3.0f);1253const uint32_t kOffscreenOffsetY =1254static_cast<uint32_t>(static_cast<double>(mTestParams.windowHeight) / 3.0f);1255const uint32_t kOffscreenWidth = kOffscreenOffsetX;1256const uint32_t kOffscreenHeight = kOffscreenOffsetY;12571258const uint32_t kOffscreenFrameWidth = static_cast<uint32_t>(1259static_cast<double>(kOffscreenWidth / static_cast<double>(kFramesPerX)));1260const uint32_t kOffscreenFrameHeight = static_cast<uint32_t>(1261static_cast<double>(kOffscreenHeight / static_cast<double>(kFramesPerY)));12621263// Add a time sample from GL and the host.1264if (mCurrentFrame == mStartFrame)1265{1266sampleTime();1267}12681269if (mParams.surfaceType == SurfaceType::Offscreen)1270{1271// Some driver (ARM and ANGLE) try to nop or defer the glFlush if it is called within the1272// renderpass to avoid breaking renderpass (performance reason). For app traces that does1273// not use any FBO, when we run in the offscreen mode, there is no frame boundary and1274// glFlush call we issued at end of frame will get skipped. To overcome this (and also1275// matches what onscreen double buffering behavior as well), we use two offscreen FBOs and1276// ping pong between them for each frame.1277glBindFramebuffer(GL_FRAMEBUFFER,1278mOffscreenFramebuffers[mTotalFrameCount % mMaxOffscreenBufferCount]);1279}12801281char frameName[32];1282sprintf(frameName, "Frame %u", mCurrentFrame);1283beginInternalTraceEvent(frameName);12841285startGpuTimer();1286mTraceLibrary->replayFrame(mCurrentFrame);1287stopGpuTimer();12881289if (mParams.surfaceType == SurfaceType::Offscreen)1290{1291if (gMinimizeGPUWork)1292{1293// To keep GPU work minimum, we skip the blit.1294glFlush();1295mOffscreenFrameCount++;1296}1297else1298{1299GLint currentDrawFBO, currentReadFBO;1300glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tDrawFBO);1301glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, ¤tReadFBO);13021303glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);1304glBindFramebuffer(1305GL_READ_FRAMEBUFFER,1306mOffscreenFramebuffers[mOffscreenFrameCount % mMaxOffscreenBufferCount]);13071308uint32_t frameX = (mOffscreenFrameCount % kFramesPerXY) % kFramesPerX;1309uint32_t frameY = (mOffscreenFrameCount % kFramesPerXY) / kFramesPerX;1310uint32_t windowX = kOffscreenOffsetX + frameX * kOffscreenFrameWidth;1311uint32_t windowY = kOffscreenOffsetY + frameY * kOffscreenFrameHeight;13121313if (gVerboseLogging)1314{1315printf("Frame %d: x %d y %d (screen x %d, screen y %d)\n", mOffscreenFrameCount,1316frameX, frameY, windowX, windowY);1317}13181319GLboolean scissorTest = GL_FALSE;1320glGetBooleanv(GL_SCISSOR_TEST, &scissorTest);13211322if (scissorTest)1323{1324glDisable(GL_SCISSOR_TEST);1325}13261327glBlitFramebuffer(0, 0, mWindowWidth, mWindowHeight, windowX, windowY,1328windowX + kOffscreenFrameWidth, windowY + kOffscreenFrameHeight,1329GL_COLOR_BUFFER_BIT, GL_NEAREST);13301331if (frameX == kFramesPerX - 1 && frameY == kFramesPerY - 1)1332{1333swap();1334glBindFramebuffer(GL_FRAMEBUFFER, 0);1335glClear(GL_COLOR_BUFFER_BIT);1336mOffscreenFrameCount = 0;1337}1338else1339{1340glFlush();1341mOffscreenFrameCount++;1342}13431344if (scissorTest)1345{1346glEnable(GL_SCISSOR_TEST);1347}1348glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentDrawFBO);1349glBindFramebuffer(GL_READ_FRAMEBUFFER, currentReadFBO);1350}13511352mTotalFrameCount++;1353}1354else1355{1356swap();1357}13581359endInternalTraceEvent(frameName);13601361if (mCurrentFrame == mEndFrame)1362{1363mTraceLibrary->resetReplay();1364mCurrentFrame = mStartFrame;1365}1366else1367{1368mCurrentFrame++;1369}13701371// Process any running queries once per iteration.1372for (size_t queryIndex = 0; queryIndex < mRunningQueries.size();)1373{1374const QueryInfo &query = mRunningQueries[queryIndex];13751376GLuint endResultAvailable = 0;1377glGetQueryObjectuivEXT(query.endTimestampQuery, GL_QUERY_RESULT_AVAILABLE,1378&endResultAvailable);13791380if (endResultAvailable == GL_TRUE)1381{1382char fboName[32];1383sprintf(fboName, "FBO %u", query.framebuffer);13841385GLint64 beginTimestamp = 0;1386glGetQueryObjecti64vEXT(query.beginTimestampQuery, GL_QUERY_RESULT, &beginTimestamp);1387glDeleteQueriesEXT(1, &query.beginTimestampQuery);1388double beginHostTime = getHostTimeFromGLTime(beginTimestamp);1389beginGLTraceEvent(fboName, beginHostTime);13901391GLint64 endTimestamp = 0;1392glGetQueryObjecti64vEXT(query.endTimestampQuery, GL_QUERY_RESULT, &endTimestamp);1393glDeleteQueriesEXT(1, &query.endTimestampQuery);1394double endHostTime = getHostTimeFromGLTime(endTimestamp);1395endGLTraceEvent(fboName, endHostTime);13961397mRunningQueries.erase(mRunningQueries.begin() + queryIndex);1398}1399else1400{1401queryIndex++;1402}1403}1404}14051406// Converts a GL timestamp into a host-side CPU time aligned with "GetHostTimeSeconds".1407// This check is necessary to line up sampled trace events in a consistent timeline.1408// Uses a linear interpolation from a series of samples. We do a blocking call to sample1409// both host and GL time once per swap. We then find the two closest GL timestamps and1410// interpolate the host times between them to compute our result. If we are past the last1411// GL timestamp we sample a new data point pair.1412double TracePerfTest::getHostTimeFromGLTime(GLint64 glTime)1413{1414// Find two samples to do a lerp.1415size_t firstSampleIndex = mTimeline.size() - 1;1416while (firstSampleIndex > 0)1417{1418if (mTimeline[firstSampleIndex].glTime < glTime)1419{1420break;1421}1422firstSampleIndex--;1423}14241425// Add an extra sample if we're missing an ending sample.1426if (firstSampleIndex == mTimeline.size() - 1)1427{1428sampleTime();1429}14301431const TimeSample &start = mTimeline[firstSampleIndex];1432const TimeSample &end = mTimeline[firstSampleIndex + 1];14331434// Note: we have observed in some odd cases later timestamps producing values that are1435// smaller than preceding timestamps. This bears further investigation.14361437// Compute the scaling factor for the lerp.1438double glDelta = static_cast<double>(glTime - start.glTime);1439double glRange = static_cast<double>(end.glTime - start.glTime);1440double t = glDelta / glRange;14411442// Lerp(t1, t2, t)1443double hostRange = end.hostTime - start.hostTime;1444return mTimeline[firstSampleIndex].hostTime + hostRange * t;1445}14461447EGLContext TracePerfTest::onEglCreateContext(EGLDisplay display,1448EGLConfig config,1449EGLContext share_context,1450EGLint const *attrib_list)1451{1452GLWindowContext newContext =1453getGLWindow()->createContextGeneric(reinterpret_cast<GLWindowContext>(share_context));1454return reinterpret_cast<EGLContext>(newContext);1455}14561457void TracePerfTest::onEglMakeCurrent(EGLDisplay display,1458EGLSurface draw,1459EGLSurface read,1460EGLContext context)1461{1462getGLWindow()->makeCurrentGeneric(reinterpret_cast<GLWindowContext>(context));1463}14641465EGLContext TracePerfTest::onEglGetCurrentContext()1466{1467return getGLWindow()->getCurrentContextGeneric();1468}14691470// Triggered when the replay calls glBindFramebuffer.1471void TracePerfTest::onReplayFramebufferChange(GLenum target, GLuint framebuffer)1472{1473if (framebuffer == 0 && mParams.surfaceType == SurfaceType::Offscreen)1474{1475glBindFramebuffer(target,1476mOffscreenFramebuffers[mTotalFrameCount % mMaxOffscreenBufferCount]);1477}1478else1479{1480glBindFramebuffer(target, framebuffer);1481}14821483switch (target)1484{1485case GL_FRAMEBUFFER:1486mDrawFramebufferBinding = framebuffer;1487mReadFramebufferBinding = framebuffer;1488break;1489case GL_DRAW_FRAMEBUFFER:1490mDrawFramebufferBinding = framebuffer;1491break;1492case GL_READ_FRAMEBUFFER:1493mReadFramebufferBinding = framebuffer;1494return;14951496default:1497UNREACHABLE();1498break;1499}15001501if (!mUseTimestampQueries)1502return;15031504// We have at most one active timestamp query at a time. This code will end the current1505// query and immediately start a new one.1506if (mCurrentQuery.beginTimestampQuery != 0)1507{1508glGenQueriesEXT(1, &mCurrentQuery.endTimestampQuery);1509glQueryCounterEXT(mCurrentQuery.endTimestampQuery, GL_TIMESTAMP_EXT);1510mRunningQueries.push_back(mCurrentQuery);1511mCurrentQuery = {};1512}15131514ASSERT(mCurrentQuery.beginTimestampQuery == 0);15151516glGenQueriesEXT(1, &mCurrentQuery.beginTimestampQuery);1517glQueryCounterEXT(mCurrentQuery.beginTimestampQuery, GL_TIMESTAMP_EXT);1518mCurrentQuery.framebuffer = framebuffer;1519}15201521std::string GetDiffPath()1522{1523#if defined(ANGLE_PLATFORM_WINDOWS)1524std::array<char, MAX_PATH> filenameBuffer = {};1525char *filenamePtr = nullptr;1526if (SearchPathA(NULL, "diff", ".exe", MAX_PATH, filenameBuffer.data(), &filenamePtr) == 0)1527{1528return "";1529}1530return std::string(filenameBuffer.data());1531#else1532return "/usr/bin/diff";1533#endif // defined(ANGLE_PLATFORM_WINDOWS)1534}15351536void PrintFileDiff(const char *aFilePath, const char *bFilePath)1537{1538std::string pathToDiff = GetDiffPath();1539if (pathToDiff.empty())1540{1541printf("Could not find diff in the path.\n");1542return;1543}15441545std::vector<const char *> args;1546args.push_back(pathToDiff.c_str());1547args.push_back(aFilePath);1548args.push_back(bFilePath);1549args.push_back("-u3");15501551printf("Calling");1552for (const char *arg : args)1553{1554printf(" %s", arg);1555}1556printf("\n");15571558ProcessHandle proc(LaunchProcess(args, ProcessOutputCapture::StdoutOnly));1559if (proc && proc->finish())1560{1561printf("\n%s\n", proc->getStdout().c_str());1562}1563}15641565void TracePerfTest::validateSerializedState(const char *expectedCapturedSerializedState,1566const char *fileName,1567uint32_t line)1568{1569if (!gTraceTestValidation)1570{1571return;1572}15731574printf("Serialization checkpoint %s:%u...\n", fileName, line);15751576const GLubyte *bytes = glGetString(GL_SERIALIZED_CONTEXT_STRING_ANGLE);1577const char *actualReplayedSerializedState = reinterpret_cast<const char *>(bytes);1578if (strcmp(expectedCapturedSerializedState, actualReplayedSerializedState) == 0)1579{1580printf("Serialization match.\n");1581return;1582}15831584printf("Serialization mismatch!\n");15851586constexpr size_t kMaxPath = 1024;1587char aFilePath[kMaxPath] = {};1588if (CreateTemporaryFile(aFilePath, kMaxPath))1589{1590printf("Saving \"expected\" capture serialization to \"%s\".\n", aFilePath);1591FILE *fpA = fopen(aFilePath, "wt");1592ASSERT(fpA);1593fprintf(fpA, "%s", expectedCapturedSerializedState);1594fclose(fpA);1595}15961597char bFilePath[kMaxPath] = {};1598if (CreateTemporaryFile(bFilePath, kMaxPath))1599{1600printf("Saving \"actual\" replay serialization to \"%s\".\n", bFilePath);1601FILE *fpB = fopen(bFilePath, "wt");1602ASSERT(fpB);1603fprintf(fpB, "%s", actualReplayedSerializedState);1604fclose(fpB);1605}16061607PrintFileDiff(aFilePath, bFilePath);1608}16091610bool TracePerfTest::isDefaultFramebuffer(GLenum target) const1611{1612switch (target)1613{1614case GL_FRAMEBUFFER:1615case GL_DRAW_FRAMEBUFFER:1616return (mDrawFramebufferBinding == 0);16171618case GL_READ_FRAMEBUFFER:1619return (mReadFramebufferBinding == 0);16201621default:1622UNREACHABLE();1623return false;1624}1625}16261627GLenum ConvertDefaultFramebufferEnum(GLenum value)1628{1629switch (value)1630{1631case GL_NONE:1632return GL_NONE;1633case GL_BACK:1634case GL_COLOR:1635return GL_COLOR_ATTACHMENT0;1636case GL_DEPTH:1637return GL_DEPTH_ATTACHMENT;1638case GL_STENCIL:1639return GL_STENCIL_ATTACHMENT;1640case GL_DEPTH_STENCIL:1641return GL_DEPTH_STENCIL_ATTACHMENT;1642default:1643UNREACHABLE();1644return GL_NONE;1645}1646}16471648std::vector<GLenum> ConvertDefaultFramebufferEnums(GLsizei numAttachments,1649const GLenum *attachments)1650{1651std::vector<GLenum> translatedAttachments;1652for (GLsizei attachmentIndex = 0; attachmentIndex < numAttachments; ++attachmentIndex)1653{1654GLenum converted = ConvertDefaultFramebufferEnum(attachments[attachmentIndex]);1655translatedAttachments.push_back(converted);1656}1657return translatedAttachments;1658}16591660// Needs special handling to treat the 0 framebuffer in offscreen mode.1661void TracePerfTest::onReplayInvalidateFramebuffer(GLenum target,1662GLsizei numAttachments,1663const GLenum *attachments)1664{1665if (mParams.surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))1666{1667glInvalidateFramebuffer(target, numAttachments, attachments);1668}1669else1670{1671std::vector<GLenum> translatedAttachments =1672ConvertDefaultFramebufferEnums(numAttachments, attachments);1673glInvalidateFramebuffer(target, numAttachments, translatedAttachments.data());1674}1675}16761677void TracePerfTest::onReplayInvalidateSubFramebuffer(GLenum target,1678GLsizei numAttachments,1679const GLenum *attachments,1680GLint x,1681GLint y,1682GLsizei width,1683GLsizei height)1684{1685if (mParams.surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))1686{1687glInvalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height);1688}1689else1690{1691std::vector<GLenum> translatedAttachments =1692ConvertDefaultFramebufferEnums(numAttachments, attachments);1693glInvalidateSubFramebuffer(target, numAttachments, translatedAttachments.data(), x, y,1694width, height);1695}1696}16971698void TracePerfTest::onReplayDrawBuffers(GLsizei n, const GLenum *bufs)1699{1700if (mParams.surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(GL_DRAW_FRAMEBUFFER))1701{1702glDrawBuffers(n, bufs);1703}1704else1705{1706std::vector<GLenum> translatedBufs = ConvertDefaultFramebufferEnums(n, bufs);1707glDrawBuffers(n, translatedBufs.data());1708}1709}17101711void TracePerfTest::onReplayReadBuffer(GLenum src)1712{1713if (mParams.surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(GL_READ_FRAMEBUFFER))1714{1715glReadBuffer(src);1716}1717else1718{1719GLenum translated = ConvertDefaultFramebufferEnum(src);1720glReadBuffer(translated);1721}1722}17231724void TracePerfTest::onReplayDiscardFramebufferEXT(GLenum target,1725GLsizei numAttachments,1726const GLenum *attachments)1727{1728if (mParams.surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))1729{1730glDiscardFramebufferEXT(target, numAttachments, attachments);1731}1732else1733{1734std::vector<GLenum> translatedAttachments =1735ConvertDefaultFramebufferEnums(numAttachments, attachments);1736glDiscardFramebufferEXT(target, numAttachments, translatedAttachments.data());1737}1738}17391740void TracePerfTest::swap()1741{1742// Capture a screenshot if enabled.1743if (gScreenShotDir != nullptr && !mScreenshotSaved &&1744static_cast<uint32_t>(gScreenShotFrame) == mCurrentFrame)1745{1746std::stringstream screenshotNameStr;1747screenshotNameStr << gScreenShotDir << GetPathSeparator() << "angle" << mBackend << "_"1748<< mStory;17491750// Add a marker to the name for any screenshot that isn't start frame1751if (mStartFrame != static_cast<uint32_t>(gScreenShotFrame))1752{1753screenshotNameStr << "_frame" << gScreenShotFrame;1754}17551756screenshotNameStr << ".png";17571758std::string screenshotName = screenshotNameStr.str();1759saveScreenshot(screenshotName);1760mScreenshotSaved = true;1761}17621763getGLWindow()->swap();1764}17651766void TracePerfTest::saveScreenshot(const std::string &screenshotName)1767{1768// The frame is already rendered and is waiting in the default framebuffer.17691770// RGBA 4-byte data.1771uint32_t pixelCount = mTestParams.windowWidth * mTestParams.windowHeight;1772std::vector<uint8_t> pixelData(pixelCount * 4);17731774// Only unbind the framebuffer on context versions where it's available.1775const TraceInfo &traceInfo = GetTraceInfo(mParams.testID);1776if (traceInfo.contextClientMajorVersion > 1)1777{1778glBindFramebuffer(GL_FRAMEBUFFER, 0);1779}17801781glReadPixels(0, 0, mTestParams.windowWidth, mTestParams.windowHeight, GL_RGBA, GL_UNSIGNED_BYTE,1782pixelData.data());17831784// Convert to RGB and flip y.1785std::vector<uint8_t> rgbData(pixelCount * 3);1786for (EGLint y = 0; y < mTestParams.windowHeight; ++y)1787{1788for (EGLint x = 0; x < mTestParams.windowWidth; ++x)1789{1790EGLint srcPixel = x + y * mTestParams.windowWidth;1791EGLint dstPixel = x + (mTestParams.windowHeight - y - 1) * mTestParams.windowWidth;1792memcpy(&rgbData[dstPixel * 3], &pixelData[srcPixel * 4], 3);1793}1794}17951796if (!angle::SavePNGRGB(screenshotName.c_str(), "ANGLE Screenshot", mTestParams.windowWidth,1797mTestParams.windowHeight, rgbData))1798{1799FAIL() << "Error saving screenshot: " << screenshotName;1800}1801else1802{1803printf("Saved screenshot: '%s'\n", screenshotName.c_str());1804}1805}18061807TracePerfParams CombineTestID(const TracePerfParams &in, RestrictedTraceID id)1808{1809const TraceInfo &traceInfo = GetTraceInfo(id);18101811TracePerfParams out = in;1812out.testID = id;1813out.majorVersion = traceInfo.contextClientMajorVersion;1814out.minorVersion = traceInfo.contextClientMinorVersion;1815out.windowWidth = traceInfo.drawSurfaceWidth;1816out.windowHeight = traceInfo.drawSurfaceHeight;1817out.colorSpace = traceInfo.drawSurfaceColorSpace;1818return out;1819}18201821TracePerfParams CombineWithSurfaceType(const TracePerfParams &in, SurfaceType surfaceType)1822{1823TracePerfParams out = in;1824out.surfaceType = surfaceType;18251826if (!IsAndroid() && surfaceType == SurfaceType::Offscreen)1827{1828out.windowWidth /= 4;1829out.windowHeight /= 4;1830}18311832// We track GPU time only in frame-rate-limited cases.1833out.trackGpuTime = surfaceType == SurfaceType::WindowWithVSync;18341835return out;1836}18371838} // anonymous namespace18391840using namespace params;1841using P = TracePerfParams;1842using PV = std::vector<P>;18431844void RegisterTraceTests()1845{1846std::vector<SurfaceType> surfaceTypes = {SurfaceType::Window};1847if (gEnableAllTraceTests)1848{1849surfaceTypes.push_back(SurfaceType::Offscreen);1850surfaceTypes.push_back(SurfaceType::WindowWithVSync);1851}18521853std::vector<ModifierFunc<P>> renderers = {Vulkan<P>, Native<P>};1854if (gEnableAllTraceTests)1855{1856if (!IsAndroid())1857{1858renderers.push_back(VulkanMockICD<P>);1859}1860renderers.push_back(VulkanSwiftShader<P>);1861}18621863PV testsWithID = CombineWithValues({P()}, AllEnums<RestrictedTraceID>(), CombineTestID);1864PV testsWithSurfaceType = CombineWithValues(testsWithID, surfaceTypes, CombineWithSurfaceType);1865PV testsWithRenderer = CombineWithFuncs(testsWithSurfaceType, renderers);1866PV filteredTests = FilterTestParams(testsWithRenderer);18671868for (const TracePerfParams ¶ms : filteredTests)1869{1870// Force on features if we're validating serialization.1871TracePerfParams overrideParams = params;1872if (gTraceTestValidation)1873{1874// Enable limits when validating traces because we usually turn off capture.1875overrideParams.eglParameters.captureLimits = EGL_TRUE;18761877// This feature should also be enabled in capture to mirror the replay.1878overrideParams.eglParameters.forceInitShaderVariables = EGL_TRUE;1879}18801881auto factory = [overrideParams]() { return new TracePerfTest(overrideParams); };1882std::string paramName = testing::PrintToString(params);1883std::stringstream testNameStr;1884testNameStr << "Run/" << paramName;1885std::string testName = testNameStr.str();1886testing::RegisterTest("TracePerfTest", testName.c_str(), nullptr, paramName.c_str(),1887__FILE__, __LINE__, factory);1888}1889}189018911892