mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Fix] (Tools/TraceReplay): show window surface after first present
This commit is contained in:
@@ -7,10 +7,12 @@
|
|||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -87,6 +89,28 @@ void *Lookup(const char *name) {
|
|||||||
return dlsym(RTLD_DEFAULT, name);
|
return dlsym(RTLD_DEFAULT, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HoldAfterTargetPresent(const char *callNo) {
|
||||||
|
const char *holdMsText = std::getenv("MOBILEGL_TRACE_HOLD_MS");
|
||||||
|
if (holdMsText == nullptr || holdMsText[0] == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int holdMs = std::atoi(holdMsText);
|
||||||
|
if (holdMs <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char *holdDone = std::getenv("MOBILEGL_TRACE_HOLD_DONE");
|
||||||
|
if (holdDone != nullptr && std::strcmp(holdDone, "1") == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char *holdCall = std::getenv("MOBILEGL_TRACE_HOLD_CALL");
|
||||||
|
if (holdCall != nullptr && holdCall[0] != '\0' && std::strcmp(holdCall, callNo) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(holdMs));
|
||||||
|
setenv("MOBILEGL_TRACE_HOLD_DONE", "1", 1);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool Load(T &slot, const char *name) {
|
bool Load(T &slot, const char *name) {
|
||||||
slot = reinterpret_cast<T>(Lookup(name));
|
slot = reinterpret_cast<T>(Lookup(name));
|
||||||
@@ -175,6 +199,7 @@ public:
|
|||||||
setenv("MOBILEGL_PRESENT_CURRENT_CALL", callNo, 1);
|
setenv("MOBILEGL_PRESENT_CURRENT_CALL", callNo, 1);
|
||||||
gEgl.swapBuffers(gDisplay, surface);
|
gEgl.swapBuffers(gDisplay, surface);
|
||||||
unsetenv("MOBILEGL_PRESENT_CURRENT_CALL");
|
unsetenv("MOBILEGL_PRESENT_CURRENT_CALL");
|
||||||
|
HoldAfterTargetPresent(callNo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -31,6 +33,10 @@
|
|||||||
|
|
||||||
extern "C" int MOBILEGL_APITRACE_RETRACE_MAIN(int argc, char** argv);
|
extern "C" int MOBILEGL_APITRACE_RETRACE_MAIN(int argc, char** argv);
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
extern "C" void mobilegl_trace_pump_events() __attribute__((weak));
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace mobilegl_trace {
|
namespace mobilegl_trace {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -148,6 +154,26 @@ bool LoadMobileGL(const Request& request, std::string& error) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigureHoldEnv(const Request& request) {
|
||||||
|
if (request.holdMs <= 0) {
|
||||||
|
unsetenv("MOBILEGL_TRACE_HOLD_MS");
|
||||||
|
unsetenv("MOBILEGL_TRACE_HOLD_CALL");
|
||||||
|
unsetenv("MOBILEGL_TRACE_HOLD_DONE");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string holdMs = std::to_string(request.holdMs);
|
||||||
|
const std::string holdCall = std::to_string(request.targetCall);
|
||||||
|
setenv("MOBILEGL_TRACE_HOLD_MS", holdMs.c_str(), 1);
|
||||||
|
setenv("MOBILEGL_TRACE_HOLD_CALL", holdCall.c_str(), 1);
|
||||||
|
unsetenv("MOBILEGL_TRACE_HOLD_DONE");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TraceHoldAlreadyRan() {
|
||||||
|
const char* holdDone = getenv("MOBILEGL_TRACE_HOLD_DONE");
|
||||||
|
return holdDone != nullptr && std::strcmp(holdDone, "1") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool CopyFile(const std::string& from, const std::string& to) {
|
bool CopyFile(const std::string& from, const std::string& to) {
|
||||||
std::ifstream input(from, std::ios::binary);
|
std::ifstream input(from, std::ios::binary);
|
||||||
std::ofstream output(to, std::ios::binary | std::ios::trunc);
|
std::ofstream output(to, std::ios::binary | std::ios::trunc);
|
||||||
@@ -436,6 +462,7 @@ int RunRetraceMain(const Request& request, bool usePresentDump) {
|
|||||||
|
|
||||||
bool RunRetrace(const Request& request, bool usePresentDump, Result& result) {
|
bool RunRetrace(const Request& request, bool usePresentDump, Result& result) {
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
ConfigureHoldEnv(request);
|
||||||
try {
|
try {
|
||||||
ScopedFdRedirect redirect(request.outputDir + "/retrace.log");
|
ScopedFdRedirect redirect(request.outputDir + "/retrace.log");
|
||||||
status = RunRetraceMain(request, usePresentDump);
|
status = RunRetraceMain(request, usePresentDump);
|
||||||
@@ -567,6 +594,28 @@ bool WriteDifferenceImage(const Result& result,
|
|||||||
return WritePngRgba(result.diffPath, diff, error);
|
return WritePngRgba(result.diffPath, diff, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PumpTraceEvents() {
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
if (mobilegl_trace_pump_events != nullptr) {
|
||||||
|
mobilegl_trace_pump_events();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void HoldAfterRetrace(const Request& request) {
|
||||||
|
if (request.holdMs <= 0 || TraceHoldAlreadyRan()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(request.holdMs);
|
||||||
|
while (std::chrono::steady_clock::now() < deadline) {
|
||||||
|
PumpTraceEvents();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||||
|
}
|
||||||
|
PumpTraceEvents();
|
||||||
|
setenv("MOBILEGL_TRACE_HOLD_DONE", "1", 1);
|
||||||
|
}
|
||||||
|
|
||||||
struct GoldenComparison {
|
struct GoldenComparison {
|
||||||
std::string path;
|
std::string path;
|
||||||
RgbaImage image;
|
RgbaImage image;
|
||||||
@@ -816,6 +865,7 @@ bool WriteResultJson(const Request& request, const Result& result) {
|
|||||||
file << " \"ssimThreshold\": " << request.ssimThreshold << ",\n";
|
file << " \"ssimThreshold\": " << request.ssimThreshold << ",\n";
|
||||||
file << " \"useAngle\": " << (UseAngleForRequest(request) ? "true" : "false") << ",\n";
|
file << " \"useAngle\": " << (UseAngleForRequest(request) ? "true" : "false") << ",\n";
|
||||||
file << " \"usePbuffer\": " << (request.usePbuffer ? "true" : "false") << ",\n";
|
file << " \"usePbuffer\": " << (request.usePbuffer ? "true" : "false") << ",\n";
|
||||||
|
file << " \"holdMs\": " << request.holdMs << ",\n";
|
||||||
file << " \"mismatchPixels\": " << result.mismatchPixels << "\n";
|
file << " \"mismatchPixels\": " << result.mismatchPixels << "\n";
|
||||||
file << "}\n";
|
file << "}\n";
|
||||||
return true;
|
return true;
|
||||||
@@ -885,9 +935,11 @@ Result RunTraceReplay(const Request& request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!RunRetrace(request, usePresentDump, result)) {
|
if (!RunRetrace(request, usePresentDump, result)) {
|
||||||
|
HoldAfterRetrace(request);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HoldAfterRetrace(request);
|
||||||
CompareWithGolden(request, result);
|
CompareWithGolden(request, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ struct Request {
|
|||||||
double ssimThreshold = 0.99;
|
double ssimThreshold = 0.99;
|
||||||
bool useAngle = false;
|
bool useAngle = false;
|
||||||
bool usePbuffer = true;
|
bool usePbuffer = true;
|
||||||
|
int holdMs = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Result {
|
struct Result {
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
#include <EGL/egl.h>
|
#include <EGL/egl.h>
|
||||||
#include <EGL/eglext.h>
|
#include <EGL/eglext.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
#include <CoreGraphics/CoreGraphics.h>
|
#include <CoreGraphics/CoreGraphics.h>
|
||||||
@@ -81,6 +83,12 @@ id SendId(id receiver, const char *selector) {
|
|||||||
return ObjcMsgSend<id (*)(id, SEL)>()(receiver, sel_registerName(selector));
|
return ObjcMsgSend<id (*)(id, SEL)>()(receiver, sel_registerName(selector));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id NSStringFromUtf8(const char *value) {
|
||||||
|
id stringClass = reinterpret_cast<id>(objc_getClass("NSString"));
|
||||||
|
return ObjcMsgSend<id (*)(id, SEL, const char *)>()(
|
||||||
|
stringClass, sel_registerName("stringWithUTF8String:"), value);
|
||||||
|
}
|
||||||
|
|
||||||
void SendVoid(id receiver, const char *selector) {
|
void SendVoid(id receiver, const char *selector) {
|
||||||
ObjcMsgSend<void (*)(id, SEL)>()(receiver, sel_registerName(selector));
|
ObjcMsgSend<void (*)(id, SEL)>()(receiver, sel_registerName(selector));
|
||||||
}
|
}
|
||||||
@@ -141,12 +149,18 @@ void PumpMacOSEvents() {
|
|||||||
SendVoid(app, "updateWindows");
|
SendVoid(app, "updateWindows");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" void mobilegl_trace_pump_events() {
|
||||||
|
PumpMacOSEvents();
|
||||||
|
}
|
||||||
|
|
||||||
void EnsureApplicationActive() {
|
void EnsureApplicationActive() {
|
||||||
id app = SharedApplication();
|
id app = SharedApplication();
|
||||||
if (!app) {
|
if (!app) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
SendVoid(app, "finishLaunching");
|
||||||
SendVoidLong(app, "setActivationPolicy:", kNSApplicationActivationPolicyRegular);
|
SendVoidLong(app, "setActivationPolicy:", kNSApplicationActivationPolicyRegular);
|
||||||
|
SendVoidId(app, "unhide:", nil);
|
||||||
SendVoidBool(app, "activateIgnoringOtherApps:", true);
|
SendVoidBool(app, "activateIgnoringOtherApps:", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,6 +186,8 @@ id CreateMetalWindow(int width, int height, id *outLayer) {
|
|||||||
std::cerr << "error: failed to create NSWindow\n";
|
std::cerr << "error: failed to create NSWindow\n";
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
SendVoidId(window, "setTitle:", NSStringFromUtf8("MobileGL Trace Replay"));
|
||||||
|
SendVoidBool(window, "setReleasedWhenClosed:", false);
|
||||||
|
|
||||||
id contentView = SendId(window, "contentView");
|
id contentView = SendId(window, "contentView");
|
||||||
id layer = SendId(metalLayerClass, "layer");
|
id layer = SendId(metalLayerClass, "layer");
|
||||||
@@ -185,14 +201,53 @@ id CreateMetalWindow(int width, int height, id *outLayer) {
|
|||||||
SendVoidCGRect(layer, "setFrame:", {{0.0, 0.0}, {surfaceWidth, surfaceHeight}});
|
SendVoidCGRect(layer, "setFrame:", {{0.0, 0.0}, {surfaceWidth, surfaceHeight}});
|
||||||
SendVoidCGSize(layer, "setDrawableSize:", {surfaceWidth, surfaceHeight});
|
SendVoidCGSize(layer, "setDrawableSize:", {surfaceWidth, surfaceHeight});
|
||||||
SendVoidId(contentView, "setLayer:", layer);
|
SendVoidId(contentView, "setLayer:", layer);
|
||||||
ObjcMsgSend<void (*)(id, SEL, id)>()(window, sel_registerName("makeKeyAndOrderFront:"), nil);
|
|
||||||
PumpMacOSEvents();
|
|
||||||
|
|
||||||
*outLayer = layer;
|
*outLayer = layer;
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShowMetalWindow(id window) {
|
||||||
|
if (!window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EnsureApplicationActive();
|
||||||
|
ObjcMsgSend<void (*)(id, SEL, id)>()(window, sel_registerName("makeKeyAndOrderFront:"), nil);
|
||||||
|
SendVoid(window, "orderFrontRegardless");
|
||||||
|
PumpMacOSEvents();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void HoldAfterTargetPresent(const char *callNo) {
|
||||||
|
const char *holdMsText = std::getenv("MOBILEGL_TRACE_HOLD_MS");
|
||||||
|
if (holdMsText == nullptr || holdMsText[0] == '\0') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int holdMs = std::atoi(holdMsText);
|
||||||
|
if (holdMs <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char *holdDone = std::getenv("MOBILEGL_TRACE_HOLD_DONE");
|
||||||
|
if (holdDone != nullptr && std::strcmp(holdDone, "1") == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char *holdCall = std::getenv("MOBILEGL_TRACE_HOLD_CALL");
|
||||||
|
if (holdCall != nullptr && holdCall[0] != '\0' && std::strcmp(holdCall, callNo) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(holdMs);
|
||||||
|
while (std::chrono::steady_clock::now() < deadline) {
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
PumpMacOSEvents();
|
||||||
|
#endif
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||||
|
}
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
PumpMacOSEvents();
|
||||||
|
#endif
|
||||||
|
setenv("MOBILEGL_TRACE_HOLD_DONE", "1", 1);
|
||||||
|
}
|
||||||
|
|
||||||
int ResolveWidth(int width) {
|
int ResolveWidth(int width) {
|
||||||
if (gRequestedWidth > 0) {
|
if (gRequestedWidth > 0) {
|
||||||
return gRequestedWidth;
|
return gRequestedWidth;
|
||||||
@@ -308,6 +363,7 @@ public:
|
|||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
id window = nil;
|
id window = nil;
|
||||||
id metalLayer = nil;
|
id metalLayer = nil;
|
||||||
|
bool windowShown = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
EglDrawable(const EglVisual *visual, int width, int height, bool pbuffer)
|
EglDrawable(const EglVisual *visual, int width, int height, bool pbuffer)
|
||||||
@@ -343,9 +399,8 @@ public:
|
|||||||
void show() override {
|
void show() override {
|
||||||
visible = true;
|
visible = true;
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
if (window) {
|
if (windowShown) {
|
||||||
ObjcMsgSend<void (*)(id, SEL, id)>()(window, sel_registerName("makeKeyAndOrderFront:"), nil);
|
ShowMetalWindow(window);
|
||||||
PumpMacOSEvents();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -366,7 +421,12 @@ public:
|
|||||||
unsetenv("MOBILEGL_PRESENT_CURRENT_CALL");
|
unsetenv("MOBILEGL_PRESENT_CURRENT_CALL");
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
PumpMacOSEvents();
|
PumpMacOSEvents();
|
||||||
|
if (window && !windowShown) {
|
||||||
|
ShowMetalWindow(window);
|
||||||
|
windowShown = true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
HoldAfterTargetPresent(callNo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -552,8 +612,10 @@ Visual *createVisual(bool doubleBuffer, unsigned samples, Profile profile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Drawable *createDrawable(const Visual *visual, int width, int height, const pbuffer_info *pbInfo) {
|
Drawable *createDrawable(const Visual *visual, int width, int height, const pbuffer_info *pbInfo) {
|
||||||
|
(void)pbInfo;
|
||||||
|
const bool usePbuffer = !TraceReplayWantsWindowSurface();
|
||||||
return new EglDrawable(static_cast<const EglVisual *>(visual), ResolveWidth(width),
|
return new EglDrawable(static_cast<const EglVisual *>(visual), ResolveWidth(width),
|
||||||
ResolveHeight(height), true);
|
ResolveHeight(height), usePbuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Context *createContext(const Visual *visual, Context *shareContext, bool) {
|
Context *createContext(const Visual *visual, Context *shareContext, bool) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ void PrintUsage(const char *argv0) {
|
|||||||
<< " --height N Replay surface height override\n"
|
<< " --height N Replay surface height override\n"
|
||||||
<< " --window-surface Replay to a native window surface\n"
|
<< " --window-surface Replay to a native window surface\n"
|
||||||
<< " --pbuffer-surface Replay to an EGL pbuffer surface (default)\n"
|
<< " --pbuffer-surface Replay to an EGL pbuffer surface (default)\n"
|
||||||
|
<< " --hold-ms N Keep the replay process alive after retrace (default: 0)\n"
|
||||||
<< " --ssim-threshold N Minimum SSIM required to pass (default: 0.99)\n"
|
<< " --ssim-threshold N Minimum SSIM required to pass (default: 0.99)\n"
|
||||||
<< " --crop-x N Compare crop x\n"
|
<< " --crop-x N Compare crop x\n"
|
||||||
<< " --crop-y N Compare crop y\n"
|
<< " --crop-y N Compare crop y\n"
|
||||||
@@ -100,6 +101,8 @@ bool ParseArgs(int argc, char **argv, mobilegl_trace::Request &request) {
|
|||||||
request.usePbuffer = false;
|
request.usePbuffer = false;
|
||||||
} else if (arg == "--pbuffer-surface") {
|
} else if (arg == "--pbuffer-surface") {
|
||||||
request.usePbuffer = true;
|
request.usePbuffer = true;
|
||||||
|
} else if (arg == "--hold-ms") {
|
||||||
|
if (!ReadInt(argc, argv, i, request.holdMs)) return false;
|
||||||
} else if (arg == "--ssim-threshold") {
|
} else if (arg == "--ssim-threshold") {
|
||||||
if (!ReadDouble(argc, argv, i, request.ssimThreshold)) return false;
|
if (!ReadDouble(argc, argv, i, request.ssimThreshold)) return false;
|
||||||
} else if (arg == "--crop-x") {
|
} else if (arg == "--crop-x") {
|
||||||
@@ -130,6 +133,10 @@ bool ParseArgs(int argc, char **argv, mobilegl_trace::Request &request) {
|
|||||||
std::cerr << "--target-call is required\n";
|
std::cerr << "--target-call is required\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (request.holdMs < 0) {
|
||||||
|
std::cerr << "--hold-ms must be non-negative\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user