[Chore] (MG_Backend, MG_Backend/DirectGLES, MG_Backend/DirectVulkan, MG_Backend/MGPipe, MG_Backend/Record, MG_Benchmark/Buffer, MG_Benchmark/Container, MG_Benchmark/Driver, MG_Benchmark/Program, MG_Benchmark/ShaderCache, MG_Benchmark/Transpile, MG_Gbm, MG_Impl, MG_Impl/CGLImpl, MG_Impl/DyldInterpose, MG_Impl/EGLImpl, MG_Impl/GLImpl, MG_Impl/GLXImpl, MG_Impl/NSOpenGLImpl, MG_Impl/Pipe, MG_Impl/WGLImpl, MG_IntegrationTest, MG_IntegrationTest/Harness, MG_IntegrationTest/Scenarios, MG_Pipe, MG_Remote, MG_Remote/Client, MG_Remote/Protocol, MG_Remote/Server, MG_Remote/Transport, MG_Remote/Wire, MG_State/EGLState, MG_State/GLState, MG_Test, MG_Test/Backend, MG_Test/BackendLoader, MG_Test/Buffer, MG_Test/EGLState, MG_Test/Framebuffer, MG_Test/Gbm, MG_Test/Pipe, MG_Test/Pipeline, MG_Test/Program, MG_Test/Query, MG_Test/SelfTest, MG_Test/ShaderTranspiler, MG_Test/State, MG_Test/Swapchain, MG_Test/Texture, MG_Test/Util, MG_Test/VertexArray, MG_Test/Wire, MG_Util/Async, MG_Util/BackendLoaders, MG_Util/Converters, MG_Util/Damage, MG_Util/Debug, MG_Util/Math, MG_Util/Metrics, MG_Util/SelfTest, MG_Util/ShaderTranspiler, MG_Util/Texture, MG_Util/X11, MobileGL, scripts/data, tools/cts, tools/device_bench, tools/spikes, tools/trace_replay, tools/wgl-smoke): clang-format every C and C++ source

- Layout only: token streams match up to split literals, continuations and using order
- The size totals fall, so the structure baseline is lowered
This commit is contained in:
2026-10-16 06:30:38 -04:00
parent 76b10cc35b
commit f4994ee36e
747 changed files with 35842 additions and 36261 deletions
+266 -347
View File
@@ -82,36 +82,31 @@ using std::vector;
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004
#endif
namespace tcu
{
namespace mobilegl
{
namespace tcu {
namespace mobilegl {
static string getLibraryName(void)
{
const char *env = std::getenv("MOBILEGL_CTS_LIB");
return (env && env[0]) ? string(env) : string("libMobileGL.so");
}
static string getLibraryName(void) {
const char* env = std::getenv("MOBILEGL_CTS_LIB");
return (env && env[0]) ? string(env) : string("libMobileGL.so");
}
#if defined(__ANDROID__)
//! Window surfaces default on: they are the only kind DirectVulkan can use
//! on Android (its pbuffer path needs VK_EXT_headless_surface).
static bool useWindowSurface(void)
{
const char *env = std::getenv("MOBILEGL_CTS_SURFACE");
return !(env && string(env) == "pbuffer");
}
//! Window surfaces default on: they are the only kind DirectVulkan can use
//! on Android (its pbuffer path needs VK_EXT_headless_surface).
static bool useWindowSurface(void) {
const char* env = std::getenv("MOBILEGL_CTS_SURFACE");
return !(env && string(env) == "pbuffer");
}
#else
//! Desktop: pbuffer only. VK_EXT_headless_surface is available there and no
//! Activity-free native window abstraction exists.
static bool useWindowSurface(void)
{
return false;
}
//! Desktop: pbuffer only. VK_EXT_headless_surface is available there and no
//! Activity-free native window abstraction exists.
static bool useWindowSurface(void) {
return false;
}
#endif
#if defined(__ANDROID__)
/*--------------------------------------------------------------------*//*!
/*--------------------------------------------------------------------*//*!
* \brief A real ANativeWindow with no Activity behind it.
*
* AImageReader's window is an ordinary BufferQueue producer, so both
@@ -119,381 +114,305 @@ static bool useWindowSurface(void)
* listener must drain the queue: without it the producer blocks once maxImages
* buffers are in flight and the next swap deadlocks.
*//*--------------------------------------------------------------------*/
class ImageReaderWindow
{
public:
ImageReaderWindow(int width, int height) : m_reader(nullptr), m_window(nullptr)
{
const media_status_t status =
AImageReader_newWithUsage(width, height, AIMAGE_FORMAT_RGBA_8888,
AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT,
kMaxImages, &m_reader);
if (status != AMEDIA_OK || m_reader == nullptr)
throw tcu::ResourceError("AImageReader_newWithUsage() failed");
class ImageReaderWindow {
public:
ImageReaderWindow(int width, int height) : m_reader(nullptr), m_window(nullptr) {
const media_status_t status = AImageReader_newWithUsage(width, height, AIMAGE_FORMAT_RGBA_8888,
AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT,
kMaxImages, &m_reader);
if (status != AMEDIA_OK || m_reader == nullptr)
throw tcu::ResourceError("AImageReader_newWithUsage() failed");
AImageReader_ImageListener listener = {this, onImageAvailable};
AImageReader_setImageListener(m_reader, &listener);
AImageReader_ImageListener listener = {this, onImageAvailable};
AImageReader_setImageListener(m_reader, &listener);
if (AImageReader_getWindow(m_reader, &m_window) != AMEDIA_OK || m_window == nullptr)
{
AImageReader_delete(m_reader);
m_reader = nullptr;
throw tcu::ResourceError("AImageReader_getWindow() failed");
}
ANativeWindow_acquire(m_window);
}
if (AImageReader_getWindow(m_reader, &m_window) != AMEDIA_OK || m_window == nullptr) {
AImageReader_delete(m_reader);
m_reader = nullptr;
throw tcu::ResourceError("AImageReader_getWindow() failed");
}
ANativeWindow_acquire(m_window);
}
~ImageReaderWindow(void)
{
if (m_window != nullptr)
ANativeWindow_release(m_window);
if (m_reader != nullptr)
{
AImageReader_setImageListener(m_reader, nullptr);
AImageReader_delete(m_reader);
}
}
~ImageReaderWindow(void) {
if (m_window != nullptr) ANativeWindow_release(m_window);
if (m_reader != nullptr) {
AImageReader_setImageListener(m_reader, nullptr);
AImageReader_delete(m_reader);
}
}
ANativeWindow *getWindow(void) const
{
return m_window;
}
ANativeWindow* getWindow(void) const { return m_window; }
private:
static const int kMaxImages = 4;
private:
static const int kMaxImages = 4;
static void onImageAvailable(void *, AImageReader *reader)
{
AImage *image = nullptr;
if (AImageReader_acquireNextImage(reader, &image) == AMEDIA_OK && image != nullptr)
AImage_delete(image);
}
static void onImageAvailable(void*, AImageReader* reader) {
AImage* image = nullptr;
if (AImageReader_acquireNextImage(reader, &image) == AMEDIA_OK && image != nullptr)
AImage_delete(image);
}
ImageReaderWindow(const ImageReaderWindow &);
ImageReaderWindow &operator=(const ImageReaderWindow &);
ImageReaderWindow(const ImageReaderWindow&);
ImageReaderWindow& operator=(const ImageReaderWindow&);
AImageReader *m_reader;
ANativeWindow *m_window;
};
AImageReader* m_reader;
ANativeWindow* m_window;
};
#else
//! Never instantiated on desktop; keeps EglRenderContext's member deletable.
class ImageReaderWindow
{
};
//! Never instantiated on desktop; keeps EglRenderContext's member deletable.
class ImageReaderWindow {};
#endif
class GetProcFuncLoader : public glw::FunctionLoader
{
public:
GetProcFuncLoader(const eglw::Library &egl) : m_egl(egl)
{
}
class GetProcFuncLoader : public glw::FunctionLoader {
public:
GetProcFuncLoader(const eglw::Library& egl) : m_egl(egl) {}
glw::GenericFuncType get(const char *name) const
{
return (glw::GenericFuncType)m_egl.getProcAddress(name);
}
glw::GenericFuncType get(const char* name) const {
return (glw::GenericFuncType)m_egl.getProcAddress(name);
}
protected:
const eglw::Library &m_egl;
};
protected:
const eglw::Library& m_egl;
};
class EglRenderContext : public glu::RenderContext
{
public:
EglRenderContext(const glu::RenderConfig &config, const tcu::CommandLine &cmdLine,
const glu::RenderContext *sharedContext);
~EglRenderContext(void);
class EglRenderContext : public glu::RenderContext {
public:
EglRenderContext(const glu::RenderConfig& config, const tcu::CommandLine& cmdLine,
const glu::RenderContext* sharedContext);
~EglRenderContext(void);
glu::ContextType getType(void) const
{
return m_contextType;
}
eglw::EGLContext getEglContext(void) const
{
return m_eglContext;
}
const glw::Functions &getFunctions(void) const
{
return m_glFunctions;
}
const tcu::RenderTarget &getRenderTarget(void) const
{
return m_renderTarget;
}
void postIterate(void);
void makeCurrent(void);
glu::ContextType getType(void) const { return m_contextType; }
eglw::EGLContext getEglContext(void) const { return m_eglContext; }
const glw::Functions& getFunctions(void) const { return m_glFunctions; }
const tcu::RenderTarget& getRenderTarget(void) const { return m_renderTarget; }
void postIterate(void);
void makeCurrent(void);
glw::GenericFuncType getProcAddress(const char *name) const
{
return (glw::GenericFuncType)m_egl.getProcAddress(name);
}
glw::GenericFuncType getProcAddress(const char* name) const {
return (glw::GenericFuncType)m_egl.getProcAddress(name);
}
private:
const eglw::DefaultLibrary m_egl;
const glu::ContextType m_contextType;
eglw::EGLDisplay m_eglDisplay;
eglw::EGLContext m_eglContext;
eglw::EGLSurface m_eglSurface;
ImageReaderWindow *m_window;
glw::Functions m_glFunctions;
tcu::RenderTarget m_renderTarget;
eglw::EGLContext m_sharedEglContext;
};
private:
const eglw::DefaultLibrary m_egl;
const glu::ContextType m_contextType;
eglw::EGLDisplay m_eglDisplay;
eglw::EGLContext m_eglContext;
eglw::EGLSurface m_eglSurface;
ImageReaderWindow* m_window;
glw::Functions m_glFunctions;
tcu::RenderTarget m_renderTarget;
eglw::EGLContext m_sharedEglContext;
};
class ContextFactory : public glu::ContextFactory
{
public:
ContextFactory(void) : glu::ContextFactory("default", "MobileGL EGL context")
{
}
class ContextFactory : public glu::ContextFactory {
public:
ContextFactory(void) : glu::ContextFactory("default", "MobileGL EGL context") {}
glu::RenderContext *createContext(const glu::RenderConfig &config, const tcu::CommandLine &cmdLine,
const glu::RenderContext *sharedContext) const
{
return new EglRenderContext(config, cmdLine, sharedContext);
}
};
glu::RenderContext* createContext(const glu::RenderConfig& config, const tcu::CommandLine& cmdLine,
const glu::RenderContext* sharedContext) const {
return new EglRenderContext(config, cmdLine, sharedContext);
}
};
class Platform : public tcu::Platform, public glu::Platform
{
public:
Platform(void)
{
m_contextFactoryRegistry.registerFactory(new ContextFactory());
}
class Platform : public tcu::Platform, public glu::Platform {
public:
Platform(void) { m_contextFactoryRegistry.registerFactory(new ContextFactory()); }
const glu::Platform &getGLPlatform(void) const
{
return *this;
}
};
const glu::Platform& getGLPlatform(void) const { return *this; }
};
EglRenderContext::EglRenderContext(const glu::RenderConfig &config, const tcu::CommandLine &cmdLine,
const glu::RenderContext *sharedContext)
: m_egl(getLibraryName().c_str())
, m_contextType(config.type)
, m_eglDisplay(EGL_NO_DISPLAY)
, m_eglContext(EGL_NO_CONTEXT)
, m_eglSurface(EGL_NO_SURFACE)
, m_window(nullptr)
, m_renderTarget(config.width, config.height,
tcu::PixelFormat(config.redBits, config.greenBits, config.blueBits, config.alphaBits),
config.depthBits, config.stencilBits, config.numSamples)
, m_sharedEglContext(EGL_NO_CONTEXT)
{
DE_UNREF(cmdLine);
EglRenderContext::EglRenderContext(const glu::RenderConfig& config, const tcu::CommandLine& cmdLine,
const glu::RenderContext* sharedContext)
: m_egl(getLibraryName().c_str()), m_contextType(config.type), m_eglDisplay(EGL_NO_DISPLAY),
m_eglContext(EGL_NO_CONTEXT), m_eglSurface(EGL_NO_SURFACE), m_window(nullptr),
m_renderTarget(config.width, config.height,
tcu::PixelFormat(config.redBits, config.greenBits, config.blueBits, config.alphaBits),
config.depthBits, config.stencilBits, config.numSamples),
m_sharedEglContext(EGL_NO_CONTEXT) {
DE_UNREF(cmdLine);
const glu::ContextType &contextType = config.type;
const bool isES = glu::isContextTypeES(contextType);
eglw::EGLint eglMajorVersion = 0;
eglw::EGLint eglMinorVersion = 0;
const glu::ContextType& contextType = config.type;
const bool isES = glu::isContextTypeES(contextType);
eglw::EGLint eglMajorVersion = 0;
eglw::EGLint eglMinorVersion = 0;
m_eglDisplay = m_egl.getDisplay(EGL_DEFAULT_DISPLAY);
EGLU_CHECK_MSG(m_egl, "eglGetDisplay()");
if (m_eglDisplay == EGL_NO_DISPLAY)
throw tcu::ResourceError("eglGetDisplay() failed");
m_eglDisplay = m_egl.getDisplay(EGL_DEFAULT_DISPLAY);
EGLU_CHECK_MSG(m_egl, "eglGetDisplay()");
if (m_eglDisplay == EGL_NO_DISPLAY) throw tcu::ResourceError("eglGetDisplay() failed");
EGLU_CHECK_CALL(m_egl, initialize(m_eglDisplay, &eglMajorVersion, &eglMinorVersion));
EGLU_CHECK_CALL(m_egl, initialize(m_eglDisplay, &eglMajorVersion, &eglMinorVersion));
// MobileGL cannot make a context current without a surface, so
// SURFACETYPE_DONT_CARE (which is what --deqp-surface-type=fbo requests)
// still gets a real one.
bool wantWindow = false;
switch (config.surfaceType)
{
case glu::RenderConfig::SURFACETYPE_WINDOW:
wantWindow = true;
break;
case glu::RenderConfig::SURFACETYPE_OFFSCREEN_NATIVE:
case glu::RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC:
wantWindow = false;
break;
case glu::RenderConfig::SURFACETYPE_DONT_CARE:
wantWindow = useWindowSurface();
break;
default:
TCU_CHECK_INTERNAL(false);
}
// MobileGL cannot make a context current without a surface, so
// SURFACETYPE_DONT_CARE (which is what --deqp-surface-type=fbo requests)
// still gets a real one.
bool wantWindow = false;
switch (config.surfaceType) {
case glu::RenderConfig::SURFACETYPE_WINDOW:
wantWindow = true;
break;
case glu::RenderConfig::SURFACETYPE_OFFSCREEN_NATIVE:
case glu::RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC:
wantWindow = false;
break;
case glu::RenderConfig::SURFACETYPE_DONT_CARE:
wantWindow = useWindowSurface();
break;
default:
TCU_CHECK_INTERNAL(false);
}
const int width = (config.width == glu::RenderConfig::DONT_CARE) ? 256 : config.width;
const int height = (config.height == glu::RenderConfig::DONT_CARE) ? 256 : config.height;
const int width = (config.width == glu::RenderConfig::DONT_CARE) ? 256 : config.width;
const int height = (config.height == glu::RenderConfig::DONT_CARE) ? 256 : config.height;
vector<eglw::EGLint> cfgAttribs;
cfgAttribs.push_back(EGL_RENDERABLE_TYPE);
if (isES)
{
switch (contextType.getMajorVersion())
{
case 3:
cfgAttribs.push_back(EGL_OPENGL_ES3_BIT);
break;
case 2:
cfgAttribs.push_back(EGL_OPENGL_ES2_BIT);
break;
default:
cfgAttribs.push_back(EGL_OPENGL_ES_BIT);
}
}
else
{
// Desktop GL, which is the whole point of this port.
cfgAttribs.push_back(EGL_OPENGL_BIT);
}
vector<eglw::EGLint> cfgAttribs;
cfgAttribs.push_back(EGL_RENDERABLE_TYPE);
if (isES) {
switch (contextType.getMajorVersion()) {
case 3:
cfgAttribs.push_back(EGL_OPENGL_ES3_BIT);
break;
case 2:
cfgAttribs.push_back(EGL_OPENGL_ES2_BIT);
break;
default:
cfgAttribs.push_back(EGL_OPENGL_ES_BIT);
}
} else {
// Desktop GL, which is the whole point of this port.
cfgAttribs.push_back(EGL_OPENGL_BIT);
}
cfgAttribs.push_back(EGL_SURFACE_TYPE);
cfgAttribs.push_back(wantWindow ? EGL_WINDOW_BIT : EGL_PBUFFER_BIT);
cfgAttribs.push_back(EGL_SURFACE_TYPE);
cfgAttribs.push_back(wantWindow ? EGL_WINDOW_BIT : EGL_PBUFFER_BIT);
static const struct
{
eglw::EGLint attrib;
int glu::RenderConfig::*field;
} s_sizeAttribs[] = {
{EGL_RED_SIZE, &glu::RenderConfig::redBits}, {EGL_GREEN_SIZE, &glu::RenderConfig::greenBits},
{EGL_BLUE_SIZE, &glu::RenderConfig::blueBits}, {EGL_ALPHA_SIZE, &glu::RenderConfig::alphaBits},
{EGL_DEPTH_SIZE, &glu::RenderConfig::depthBits}, {EGL_STENCIL_SIZE, &glu::RenderConfig::stencilBits},
{EGL_SAMPLES, &glu::RenderConfig::numSamples},
};
for (size_t ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_sizeAttribs); ndx++)
{
const int value = config.*(s_sizeAttribs[ndx].field);
if (value != glu::RenderConfig::DONT_CARE)
{
cfgAttribs.push_back(s_sizeAttribs[ndx].attrib);
cfgAttribs.push_back(value);
}
}
cfgAttribs.push_back(EGL_NONE);
static const struct {
eglw::EGLint attrib;
int glu::RenderConfig::* field;
} s_sizeAttribs[] = {
{EGL_RED_SIZE, &glu::RenderConfig::redBits}, {EGL_GREEN_SIZE, &glu::RenderConfig::greenBits},
{EGL_BLUE_SIZE, &glu::RenderConfig::blueBits}, {EGL_ALPHA_SIZE, &glu::RenderConfig::alphaBits},
{EGL_DEPTH_SIZE, &glu::RenderConfig::depthBits}, {EGL_STENCIL_SIZE, &glu::RenderConfig::stencilBits},
{EGL_SAMPLES, &glu::RenderConfig::numSamples},
};
for (size_t ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_sizeAttribs); ndx++) {
const int value = config.*(s_sizeAttribs[ndx].field);
if (value != glu::RenderConfig::DONT_CARE) {
cfgAttribs.push_back(s_sizeAttribs[ndx].attrib);
cfgAttribs.push_back(value);
}
}
cfgAttribs.push_back(EGL_NONE);
eglw::EGLConfig eglConfig = nullptr;
eglw::EGLint numConfigs = 0;
EGLU_CHECK_CALL(m_egl, chooseConfig(m_eglDisplay, &cfgAttribs[0], &eglConfig, 1, &numConfigs));
if (numConfigs < 1)
throw tcu::NotSupportedError("No matching EGL config for the requested context");
eglw::EGLConfig eglConfig = nullptr;
eglw::EGLint numConfigs = 0;
EGLU_CHECK_CALL(m_egl, chooseConfig(m_eglDisplay, &cfgAttribs[0], &eglConfig, 1, &numConfigs));
if (numConfigs < 1) throw tcu::NotSupportedError("No matching EGL config for the requested context");
if (wantWindow)
{
if (wantWindow) {
#if defined(__ANDROID__)
m_window = new ImageReaderWindow(width, height);
m_window = new ImageReaderWindow(width, height);
eglw::EGLint visualId = 0;
if (m_egl.getConfigAttrib(m_eglDisplay, eglConfig, EGL_NATIVE_VISUAL_ID, &visualId) && visualId != 0)
ANativeWindow_setBuffersGeometry(m_window->getWindow(), width, height, visualId);
eglw::EGLint visualId = 0;
if (m_egl.getConfigAttrib(m_eglDisplay, eglConfig, EGL_NATIVE_VISUAL_ID, &visualId) && visualId != 0)
ANativeWindow_setBuffersGeometry(m_window->getWindow(), width, height, visualId);
m_eglSurface = m_egl.createWindowSurface(m_eglDisplay, eglConfig,
(eglw::EGLNativeWindowType)m_window->getWindow(), nullptr);
EGLU_CHECK_MSG(m_egl, "eglCreateWindowSurface()");
m_eglSurface = m_egl.createWindowSurface(m_eglDisplay, eglConfig,
(eglw::EGLNativeWindowType)m_window->getWindow(), nullptr);
EGLU_CHECK_MSG(m_egl, "eglCreateWindowSurface()");
#else
throw tcu::NotSupportedError("Window surfaces are not supported by the desktop MobileGL platform");
throw tcu::NotSupportedError("Window surfaces are not supported by the desktop MobileGL platform");
#endif
}
else
{
const eglw::EGLint surfaceAttribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};
m_eglSurface = m_egl.createPbufferSurface(m_eglDisplay, eglConfig, surfaceAttribs);
EGLU_CHECK_MSG(m_egl, "eglCreatePbufferSurface()");
}
} else {
const eglw::EGLint surfaceAttribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};
m_eglSurface = m_egl.createPbufferSurface(m_eglDisplay, eglConfig, surfaceAttribs);
EGLU_CHECK_MSG(m_egl, "eglCreatePbufferSurface()");
}
if (m_eglSurface == EGL_NO_SURFACE)
throw tcu::ResourceError("Failed to create EGL surface");
if (m_eglSurface == EGL_NO_SURFACE) throw tcu::ResourceError("Failed to create EGL surface");
vector<eglw::EGLint> ctxAttribs;
ctxAttribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
ctxAttribs.push_back(contextType.getMajorVersion());
ctxAttribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
ctxAttribs.push_back(contextType.getMinorVersion());
vector<eglw::EGLint> ctxAttribs;
ctxAttribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
ctxAttribs.push_back(contextType.getMajorVersion());
ctxAttribs.push_back(EGL_CONTEXT_MINOR_VERSION_KHR);
ctxAttribs.push_back(contextType.getMinorVersion());
switch (contextType.getProfile())
{
case glu::PROFILE_ES:
EGLU_CHECK_CALL(m_egl, bindAPI(EGL_OPENGL_ES_API));
break;
case glu::PROFILE_CORE:
EGLU_CHECK_CALL(m_egl, bindAPI(EGL_OPENGL_API));
ctxAttribs.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR);
ctxAttribs.push_back(EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR);
break;
case glu::PROFILE_COMPATIBILITY:
EGLU_CHECK_CALL(m_egl, bindAPI(EGL_OPENGL_API));
ctxAttribs.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR);
ctxAttribs.push_back(EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR);
break;
default:
TCU_CHECK_INTERNAL(false);
}
switch (contextType.getProfile()) {
case glu::PROFILE_ES:
EGLU_CHECK_CALL(m_egl, bindAPI(EGL_OPENGL_ES_API));
break;
case glu::PROFILE_CORE:
EGLU_CHECK_CALL(m_egl, bindAPI(EGL_OPENGL_API));
ctxAttribs.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR);
ctxAttribs.push_back(EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR);
break;
case glu::PROFILE_COMPATIBILITY:
EGLU_CHECK_CALL(m_egl, bindAPI(EGL_OPENGL_API));
ctxAttribs.push_back(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR);
ctxAttribs.push_back(EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR);
break;
default:
TCU_CHECK_INTERNAL(false);
}
eglw::EGLint flags = 0;
if ((contextType.getFlags() & glu::CONTEXT_DEBUG) != 0)
flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
if ((contextType.getFlags() & glu::CONTEXT_ROBUST) != 0)
flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
if ((contextType.getFlags() & glu::CONTEXT_FORWARD_COMPATIBLE) != 0)
flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
if (flags != 0)
{
ctxAttribs.push_back(EGL_CONTEXT_FLAGS_KHR);
ctxAttribs.push_back(flags);
}
ctxAttribs.push_back(EGL_NONE);
eglw::EGLint flags = 0;
if ((contextType.getFlags() & glu::CONTEXT_DEBUG) != 0) flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
if ((contextType.getFlags() & glu::CONTEXT_ROBUST) != 0) flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
if ((contextType.getFlags() & glu::CONTEXT_FORWARD_COMPATIBLE) != 0)
flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
if (flags != 0) {
ctxAttribs.push_back(EGL_CONTEXT_FLAGS_KHR);
ctxAttribs.push_back(flags);
}
ctxAttribs.push_back(EGL_NONE);
const EglRenderContext *sharedEglRenderContext = dynamic_cast<const EglRenderContext *>(sharedContext);
m_sharedEglContext = sharedEglRenderContext ? sharedEglRenderContext->getEglContext() : EGL_NO_CONTEXT;
const EglRenderContext* sharedEglRenderContext = dynamic_cast<const EglRenderContext*>(sharedContext);
m_sharedEglContext = sharedEglRenderContext ? sharedEglRenderContext->getEglContext() : EGL_NO_CONTEXT;
m_eglContext = m_egl.createContext(m_eglDisplay, eglConfig, m_sharedEglContext, &ctxAttribs[0]);
EGLU_CHECK_MSG(m_egl, "eglCreateContext()");
if (!m_eglContext)
throw tcu::ResourceError("eglCreateContext() failed");
m_eglContext = m_egl.createContext(m_eglDisplay, eglConfig, m_sharedEglContext, &ctxAttribs[0]);
EGLU_CHECK_MSG(m_egl, "eglCreateContext()");
if (!m_eglContext) throw tcu::ResourceError("eglCreateContext() failed");
// MobileGL requires draw == read.
EGLU_CHECK_CALL(m_egl, makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext));
// MobileGL requires draw == read.
EGLU_CHECK_CALL(m_egl, makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext));
// MobileGL advertises EGL 1.5, so eglGetProcAddress resolves core entry
// points too; there is no separate GL library to dlopen.
GetProcFuncLoader funcLoader(m_egl);
glu::initCoreFunctions(&m_glFunctions, &funcLoader, contextType.getAPI());
glu::initExtensionFunctions(&m_glFunctions, &funcLoader, contextType.getAPI());
}
EglRenderContext::~EglRenderContext(void)
{
try
{
if (m_eglDisplay != EGL_NO_DISPLAY)
{
m_egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (m_eglContext != EGL_NO_CONTEXT)
m_egl.destroyContext(m_eglDisplay, m_eglContext);
if (m_eglSurface != EGL_NO_SURFACE)
m_egl.destroySurface(m_eglDisplay, m_eglSurface);
if (m_sharedEglContext == EGL_NO_CONTEXT)
m_egl.terminate(m_eglDisplay);
// MobileGL advertises EGL 1.5, so eglGetProcAddress resolves core entry
// points too; there is no separate GL library to dlopen.
GetProcFuncLoader funcLoader(m_egl);
glu::initCoreFunctions(&m_glFunctions, &funcLoader, contextType.getAPI());
glu::initExtensionFunctions(&m_glFunctions, &funcLoader, contextType.getAPI());
}
}
catch (...)
{
}
delete m_window;
}
EglRenderContext::~EglRenderContext(void) {
try {
if (m_eglDisplay != EGL_NO_DISPLAY) {
m_egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
void EglRenderContext::makeCurrent(void)
{
EGLU_CHECK_CALL(m_egl, makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext));
}
if (m_eglContext != EGL_NO_CONTEXT) m_egl.destroyContext(m_eglDisplay, m_eglContext);
void EglRenderContext::postIterate(void)
{
m_glFunctions.finish();
}
if (m_eglSurface != EGL_NO_SURFACE) m_egl.destroySurface(m_eglDisplay, m_eglSurface);
} // namespace mobilegl
if (m_sharedEglContext == EGL_NO_CONTEXT) m_egl.terminate(m_eglDisplay);
}
}
catch (...) {
}
delete m_window;
}
void EglRenderContext::makeCurrent(void) {
EGLU_CHECK_CALL(m_egl, makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext));
}
void EglRenderContext::postIterate(void) {
m_glFunctions.finish();
}
} // namespace mobilegl
} // namespace tcu
tcu::Platform *createPlatform(void)
{
tcu::Platform* createPlatform(void) {
return new tcu::mobilegl::Platform();
}
+4 -5
View File
@@ -23,11 +23,10 @@
#include "tcuDefs.hpp"
namespace tcu
{
class Platform;
}
namespace tcu {
class Platform;
} // namespace tcu
tcu::Platform *createPlatform(void);
tcu::Platform* createPlatform(void);
#endif // _TCUMOBILEGLPLATFORM_HPP
+76 -63
View File
@@ -33,15 +33,15 @@
#include <string.h>
#include <unistd.h>
typedef void *EGLDisplay;
typedef void *EGLConfig;
typedef void *EGLSurface;
typedef void *EGLContext;
typedef void* EGLDisplay;
typedef void* EGLConfig;
typedef void* EGLSurface;
typedef void* EGLContext;
typedef int EGLint;
typedef unsigned int EGLBoolean;
typedef unsigned int EGLenum;
typedef void *EGLNativeDisplayType;
typedef void *EGLNativeWindowType;
typedef void* EGLNativeDisplayType;
typedef void* EGLNativeWindowType;
#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0)
#define EGL_NO_CONTEXT ((EGLContext)0)
@@ -88,43 +88,45 @@ typedef void *EGLNativeWindowType;
#define GL_RENDERBUFFER 0x8D41
typedef EGLDisplay (*P_getDisplay)(EGLNativeDisplayType);
typedef EGLBoolean (*P_initialize)(EGLDisplay, EGLint *, EGLint *);
typedef EGLBoolean (*P_initialize)(EGLDisplay, EGLint*, EGLint*);
typedef EGLBoolean (*P_bindAPI)(EGLenum);
typedef EGLBoolean (*P_chooseConfig)(EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *);
typedef EGLBoolean (*P_getConfigAttrib)(EGLDisplay, EGLConfig, EGLint, EGLint *);
typedef EGLSurface (*P_createWindowSurface)(EGLDisplay, EGLConfig, EGLNativeWindowType, const EGLint *);
typedef EGLSurface (*P_createPbufferSurface)(EGLDisplay, EGLConfig, const EGLint *);
typedef EGLContext (*P_createContext)(EGLDisplay, EGLConfig, EGLContext, const EGLint *);
typedef EGLBoolean (*P_chooseConfig)(EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*);
typedef EGLBoolean (*P_getConfigAttrib)(EGLDisplay, EGLConfig, EGLint, EGLint*);
typedef EGLSurface (*P_createWindowSurface)(EGLDisplay, EGLConfig, EGLNativeWindowType, const EGLint*);
typedef EGLSurface (*P_createPbufferSurface)(EGLDisplay, EGLConfig, const EGLint*);
typedef EGLContext (*P_createContext)(EGLDisplay, EGLConfig, EGLContext, const EGLint*);
typedef EGLBoolean (*P_makeCurrent)(EGLDisplay, EGLSurface, EGLSurface, EGLContext);
typedef EGLint (*P_getError)(void);
typedef const unsigned char *(*P_glGetString)(unsigned int);
typedef void (*P_glGetIntegerv)(unsigned int, int *);
typedef const unsigned char* (*P_glGetString)(unsigned int);
typedef void (*P_glGetIntegerv)(unsigned int, int*);
typedef void (*P_glClearColor)(float, float, float, float);
typedef void (*P_glClear)(unsigned int);
typedef void (*P_glFinish)(void);
typedef void (*P_glReadPixels)(int, int, int, int, unsigned int, unsigned int, void *);
typedef void (*P_glReadPixels)(int, int, int, int, unsigned int, unsigned int, void*);
typedef unsigned int (*P_glGetError)(void);
typedef void (*P_glGenTextures)(int, unsigned int *);
typedef void (*P_glGenTextures)(int, unsigned int*);
typedef void (*P_glBindTexture)(unsigned int, unsigned int);
typedef void (*P_glTexImage2D)(unsigned int, int, int, int, int, int, unsigned int, unsigned int, const void *);
typedef void (*P_glTexImage2D)(unsigned int, int, int, int, int, int, unsigned int, unsigned int, const void*);
typedef void (*P_glTexParameteri)(unsigned int, unsigned int, int);
typedef void (*P_glGenFramebuffers)(int, unsigned int *);
typedef void (*P_glGenFramebuffers)(int, unsigned int*);
typedef void (*P_glBindFramebuffer)(unsigned int, unsigned int);
typedef void (*P_glFramebufferTexture2D)(unsigned int, unsigned int, unsigned int, unsigned int, int);
typedef unsigned int (*P_glCheckFramebufferStatus)(unsigned int);
typedef void (*P_glViewport)(int, int, int, int);
typedef void (*P_glGenRenderbuffers)(int, unsigned int *);
typedef void (*P_glGenRenderbuffers)(int, unsigned int*);
typedef void (*P_glBindRenderbuffer)(unsigned int, unsigned int);
typedef void (*P_glRenderbufferStorage)(unsigned int, unsigned int, int, int);
typedef void (*P_glFramebufferRenderbuffer)(unsigned int, unsigned int, unsigned int, unsigned int);
static void *g_lib;
static void *S(const char *n) { return dlsym(g_lib, n); }
static void* g_lib;
static void* S(const char* n) {
return dlsym(g_lib, n);
}
static void on_image(void *ctx, AImageReader *r) {
static void on_image(void* ctx, AImageReader* r) {
(void)ctx;
AImage *img = NULL;
AImage* img = NULL;
/* Drain the queue, or the producer blocks once maxImages are in flight. */
if (AImageReader_acquireNextImage(r, &img) == AMEDIA_OK && img) AImage_delete(img);
}
@@ -136,18 +138,23 @@ static int near8(unsigned got, int want, int tol) {
return d <= tol && d >= -tol;
}
int main(int argc, char **argv) {
const char *backend = "DirectGLES";
const char *surface = "pbuffer";
const char *libpath = "libMobileGL.so";
int main(int argc, char** argv) {
const char* backend = "DirectGLES";
const char* surface = "pbuffer";
const char* libpath = "libMobileGL.so";
for (int i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "--backend") && i + 1 < argc) backend = argv[++i];
else if (!strcmp(argv[i], "--surface") && i + 1 < argc) surface = argv[++i];
else if (!strcmp(argv[i], "--lib") && i + 1 < argc) libpath = argv[++i];
if (!strcmp(argv[i], "--backend") && i + 1 < argc)
backend = argv[++i];
else if (!strcmp(argv[i], "--surface") && i + 1 < argc)
surface = argv[++i];
else if (!strcmp(argv[i], "--lib") && i + 1 < argc)
libpath = argv[++i];
else {
fprintf(stderr, "usage: %s [--backend DirectGLES|DirectVulkan]"
" [--surface pbuffer|imagereader] [--lib path]\n", argv[0]);
fprintf(stderr,
"usage: %s [--backend DirectGLES|DirectVulkan]"
" [--surface pbuffer|imagereader] [--lib path]\n",
argv[0]);
return 2;
}
}
@@ -159,13 +166,13 @@ int main(int argc, char **argv) {
printf("mgprobe backend=%s surface=%s lib=%s\n", backend, surface, libpath);
int useWindow = !strcmp(surface, "imagereader");
ANativeWindow *win = NULL;
AImageReader *reader = NULL;
ANativeWindow* win = NULL;
AImageReader* reader = NULL;
if (useWindow) {
if (AImageReader_newWithUsage(DIM, DIM, AIMAGE_FORMAT_RGBA_8888,
AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT,
4, &reader) != AMEDIA_OK || !reader) {
AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT,
4, &reader) != AMEDIA_OK ||
!reader) {
printf("FAIL AImageReader_newWithUsage\n");
return 3;
}
@@ -210,12 +217,23 @@ int main(int argc, char **argv) {
return 7;
}
const EGLint cfgAttribs[] = {
EGL_SURFACE_TYPE, useWindow ? EGL_WINDOW_BIT : EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 24, EGL_STENCIL_SIZE, 8,
EGL_NONE};
const EGLint cfgAttribs[] = {EGL_SURFACE_TYPE,
useWindow ? EGL_WINDOW_BIT : EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE,
EGL_OPENGL_BIT,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
8,
EGL_DEPTH_SIZE,
24,
EGL_STENCIL_SIZE,
8,
EGL_NONE};
EGLConfig cfg = 0;
EGLint ncfg = 0;
if (!eglChooseConfig_(dpy, cfgAttribs, &cfg, 1, &ncfg) || ncfg < 1) {
@@ -234,14 +252,13 @@ int main(int argc, char **argv) {
surf = eglCreatePbufferSurface_(dpy, cfg, sa);
}
if (surf == EGL_NO_SURFACE) {
printf("FAIL create%sSurface err=0x%x\n", useWindow ? "Window" : "Pbuffer",
eglGetError_ ? eglGetError_() : 0);
printf("FAIL create%sSurface err=0x%x\n", useWindow ? "Window" : "Pbuffer", eglGetError_ ? eglGetError_() : 0);
return 9;
}
const EGLint ctxAttribs[] = {
EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION, 3,
EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, EGL_NONE};
EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION, 3, EGL_CONTEXT_OPENGL_PROFILE_MASK,
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, EGL_NONE};
EGLContext ctx = eglCreateContext_(dpy, cfg, EGL_NO_CONTEXT, ctxAttribs);
if (ctx == EGL_NO_CONTEXT) {
printf("FAIL eglCreateContext(3.3 core) err=0x%x\n", eglGetError_ ? eglGetError_() : 0);
@@ -272,18 +289,18 @@ int main(int argc, char **argv) {
P_glGenRenderbuffers glGenRenderbuffers_ = (P_glGenRenderbuffers)S("glGenRenderbuffers");
P_glBindRenderbuffer glBindRenderbuffer_ = (P_glBindRenderbuffer)S("glBindRenderbuffer");
P_glRenderbufferStorage glRenderbufferStorage_ = (P_glRenderbufferStorage)S("glRenderbufferStorage");
P_glFramebufferRenderbuffer glFramebufferRenderbuffer_ = (P_glFramebufferRenderbuffer)S("glFramebufferRenderbuffer");
P_glFramebufferRenderbuffer glFramebufferRenderbuffer_ =
(P_glFramebufferRenderbuffer)S("glFramebufferRenderbuffer");
int major = -1, minor = -1, profile = -1;
glGetIntegerv_(GL_MAJOR_VERSION, &major);
glGetIntegerv_(GL_MINOR_VERSION, &minor);
glGetIntegerv_(GL_CONTEXT_PROFILE_MASK, &profile);
printf(" GL_VENDOR %s\n", (const char *)glGetString_(GL_VENDOR));
printf(" GL_RENDERER %s\n", (const char *)glGetString_(GL_RENDERER));
printf(" GL_VERSION %s\n", (const char *)glGetString_(GL_VERSION));
printf(" GLSL %s\n", (const char *)glGetString_(GL_SHADING_LANGUAGE_VERSION));
printf(" version %d.%d profile_mask 0x%x %s\n", major, minor, profile,
(profile & 1) ? "(core)" : "(NOT CORE)");
printf(" GL_VENDOR %s\n", (const char*)glGetString_(GL_VENDOR));
printf(" GL_RENDERER %s\n", (const char*)glGetString_(GL_RENDERER));
printf(" GL_VERSION %s\n", (const char*)glGetString_(GL_VERSION));
printf(" GLSL %s\n", (const char*)glGetString_(GL_SHADING_LANGUAGE_VERSION));
printf(" version %d.%d profile_mask 0x%x %s\n", major, minor, profile, (profile & 1) ? "(core)" : "(NOT CORE)");
unsigned char px[4];
@@ -294,8 +311,7 @@ int main(int argc, char **argv) {
memset(px, 0, sizeof px);
glReadPixels_(DIM / 2, DIM / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, px);
int defOk = near8(px[0], 64, 10) && near8(px[1], 128, 10) && near8(px[2], 191, 10);
printf(" default-FB readback (%u,%u,%u,%u) %s\n", px[0], px[1], px[2], px[3],
defOk ? "ok" : "BROKEN");
printf(" default-FB readback (%u,%u,%u,%u) %s\n", px[0], px[1], px[2], px[3], defOk ? "ok" : "BROKEN");
/* User FBO - this is what dEQP uses with --deqp-surface-type=fbo. */
unsigned int tex = 0, fbo = 0;
@@ -317,8 +333,7 @@ int main(int argc, char **argv) {
memset(px, 0, sizeof px);
glReadPixels_(DIM / 2, DIM / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, px);
fboOk = near8(px[0], 230, 10) && near8(px[1], 51, 10) && near8(px[2], 102, 10);
printf(" user-FBO readback (%u,%u,%u,%u) %s\n", px[0], px[1], px[2], px[3],
fboOk ? "ok" : "BROKEN");
printf(" user-FBO readback (%u,%u,%u,%u) %s\n", px[0], px[1], px[2], px[3], fboOk ? "ok" : "BROKEN");
} else {
printf(" user-FBO incomplete status=0x%x\n", fbst);
}
@@ -346,8 +361,7 @@ int main(int argc, char **argv) {
memset(px, 0, sizeof px);
glReadPixels_(DIM / 2, DIM / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, px);
rboOk = near8(px[0], 26, 10) && near8(px[1], 179, 10) && near8(px[2], 77, 10);
printf(" rbo-FBO readback (%u,%u,%u,%u) %s\n", px[0], px[1], px[2], px[3],
rboOk ? "ok" : "BROKEN");
printf(" rbo-FBO readback (%u,%u,%u,%u) %s\n", px[0], px[1], px[2], px[3], rboOk ? "ok" : "BROKEN");
} else {
printf(" rbo-FBO incomplete status=0x%x\n", rst);
}
@@ -357,9 +371,8 @@ int main(int argc, char **argv) {
unsigned glerr = glGetError_ ? glGetError_() : 0;
int ok = fboOk && rboOk && (major > 3 || (major == 3 && minor >= 3)) && (profile & 1) && glerr == 0;
printf("%s backend=%s surface=%s default_fb=%s user_fbo=%s rbo_fbo=%s glerr=0x%x\n",
ok ? "PASS" : "FAIL", backend, surface, defOk ? "ok" : "broken",
fboOk ? "ok" : "broken", rboOk ? "ok" : "broken", glerr);
printf("%s backend=%s surface=%s default_fb=%s user_fbo=%s rbo_fbo=%s glerr=0x%x\n", ok ? "PASS" : "FAIL", backend,
surface, defOk ? "ok" : "broken", fboOk ? "ok" : "broken", rboOk ? "ok" : "broken", glerr);
fflush(stdout);
/* MobileGL aborts in static teardown; leave before that runs. */
+4 -2
View File
@@ -114,8 +114,10 @@ static int ScanPids(const char* pkg, int* pids) {
static void Push(long long t, int index, long long cpu) {
struct Sample* s = &ring[(ringHead + ringSize) % ringCap];
if (ringSize == ringCap) ringHead = (ringHead + 1) % ringCap;
else ++ringSize;
if (ringSize == ringCap)
ringHead = (ringHead + 1) % ringCap;
else
++ringSize;
s->t = t;
s->index = index;
s->cpu = cpu;
+72 -40
View File
@@ -21,7 +21,10 @@
#include <unistd.h>
static volatile sig_atomic_t g_stop = 0;
static void onsig(int s) { (void)s; g_stop = 1; }
static void onsig(int s) {
(void)s;
g_stop = 1;
}
#define MAXT 128
#define MAXSY 400
@@ -29,14 +32,14 @@ static void onsig(int s) { (void)s; g_stop = 1; }
struct T {
int tid;
char name[32];
long long cpu0; // utime+stime at start (ticks)
long long vcs0; // nr_voluntary_switches at start
long long run; // samples with syscall "running"
long long sys[MAXSY]; // samples while in syscall nr
long long cpu0; // utime+stime at start (ticks)
long long vcs0; // nr_voluntary_switches at start
long long run; // samples with syscall "running"
long long sys[MAXSY]; // samples while in syscall nr
long long openerr;
};
static long long readfile(const char *p, char *buf, int n) {
static long long readfile(const char* p, char* buf, int n) {
int fd = open(p, O_RDONLY);
if (fd < 0) return -1;
long long r = read(fd, buf, n - 1);
@@ -45,12 +48,15 @@ static long long readfile(const char *p, char *buf, int n) {
return r;
}
int main(int argc, char **argv) {
if (argc < 4) { fprintf(stderr, "usage: sampler pid dur_s interval_ms out\n"); return 2; }
int main(int argc, char** argv) {
if (argc < 4) {
fprintf(stderr, "usage: sampler pid dur_s interval_ms out\n");
return 2;
}
int pid = atoi(argv[1]);
int durMs = atoi(argv[2]) * 1000;
int intMs = argc > 4 ? atoi(argv[3]) : 2;
const char *out = argc > 4 ? argv[4] : "sampler.csv";
const char* out = argc > 4 ? argv[4] : "sampler.csv";
struct T ts[MAXT];
int n = 0;
@@ -59,35 +65,45 @@ int main(int argc, char **argv) {
// initial scan
for (int pass = 0; pass < 2; pass++) {
snprintf(path, sizeof path, "/proc/%d/task", pid);
DIR *d = opendir(path);
if (!d) { perror("opendir task"); return 1; }
struct dirent *e;
DIR* d = opendir(path);
if (!d) {
perror("opendir task");
return 1;
}
struct dirent* e;
n = 0;
while ((e = readdir(d)) && n < MAXT) {
if (e->d_name[0] == '.') continue;
int tid = atoi(e->d_name);
if (tid <= 0) continue;
struct T *t = &ts[n++];
struct T* t = &ts[n++];
memset(t, 0, sizeof *t);
t->tid = tid;
snprintf(path, sizeof path, "/proc/%d/task/%d/comm", pid, tid);
if (readfile(path, buf, sizeof buf) > 0) {
strncpy(t->name, buf, sizeof t->name - 1);
char *nl = strchr(t->name, '\n'); if (nl) *nl = 0;
char* nl = strchr(t->name, '\n');
if (nl) *nl = 0;
}
snprintf(path, sizeof path, "/proc/%d/task/%d/stat", pid, tid);
long long ut = 0, st = 0;
if (readfile(path, buf, sizeof buf) > 0) {
// fields: 1 pid, 2 comm, 3 state ... 14 utime, 15 stime.
// tokens after ')' start at field 3, so utime is f==11, stime f==12.
char *r = strrchr(buf, ')');
char* r = strrchr(buf, ')');
if (r) {
int f = 0; char *sp = r + 1;
int f = 0;
char* sp = r + 1;
while (*sp) {
if (f == 11) ut = atoll(sp);
if (f == 12) { st = atoll(sp); break; }
while (*sp && *sp != ' ') sp++;
while (*sp == ' ') sp++;
if (f == 12) {
st = atoll(sp);
break;
}
while (*sp && *sp != ' ')
sp++;
while (*sp == ' ')
sp++;
f++;
}
}
@@ -96,15 +112,15 @@ int main(int argc, char **argv) {
snprintf(path, sizeof path, "/proc/%d/task/%d/sched", pid, tid);
t->vcs0 = -1;
if (readfile(path, buf, sizeof buf) > 0) {
char *v = strstr(buf, "nr_voluntary_switches");
char* v = strstr(buf, "nr_voluntary_switches");
if (v) {
char *col = strchr(v, ':');
char* col = strchr(v, ':');
if (col) t->vcs0 = atoll(col + 1);
}
}
}
closedir(d);
if (n > 1) break; // first pass may catch a dying thread set
if (n > 1) break; // first pass may catch a dying thread set
usleep(50000);
}
@@ -118,11 +134,20 @@ int main(int argc, char **argv) {
for (int i = 0; i < n; i++) {
snprintf(path, sizeof path, "/proc/%d/task/%d/syscall", pid, ts[i].tid);
long long r = readfile(path, buf, sizeof buf);
if (r <= 0) { ts[i].openerr++; continue; }
if (!strncmp(buf, "running", 7)) { ts[i].run++; continue; }
if (r <= 0) {
ts[i].openerr++;
continue;
}
if (!strncmp(buf, "running", 7)) {
ts[i].run++;
continue;
}
long nr = -2;
if (buf[0] != '-') nr = strtol(buf, NULL, 10);
if (nr < 0) { ts[i].run++; continue; } // -1 means running
if (nr < 0) {
ts[i].run++;
continue;
} // -1 means running
if (nr >= MAXSY) nr = MAXSY - 1;
ts[i].sys[nr]++;
}
@@ -131,24 +156,32 @@ int main(int argc, char **argv) {
elapsed = (now.tv_sec - t0.tv_sec) * 1000LL + (now.tv_nsec - t0.tv_nsec) / 1000000LL;
}
FILE *f = fopen(out, "w");
if (!f) { perror("fopen out"); return 1; }
fprintf(f, "# pid=%d dur_ms=%lld interval_ms=%d samples_per_thread~=%lld\n",
pid, elapsed, intMs, elapsed / intMs);
FILE* f = fopen(out, "w");
if (!f) {
perror("fopen out");
return 1;
}
fprintf(f, "# pid=%d dur_ms=%lld interval_ms=%d samples_per_thread~=%lld\n", pid, elapsed, intMs, elapsed / intMs);
fprintf(f, "tid,name,cpu_ticks_delta,vcs_delta,run_samples,open_err,sys_hist\n");
for (int i = 0; i < n; i++) {
struct T *t = &ts[i];
struct T* t = &ts[i];
long long ut = 0, st = 0;
snprintf(path, sizeof path, "/proc/%d/task/%d/stat", pid, t->tid);
if (readfile(path, buf, sizeof buf) > 0) {
char *r = strrchr(buf, ')');
char* r = strrchr(buf, ')');
if (r) {
int f2 = 0; char *sp = r + 1;
int f2 = 0;
char* sp = r + 1;
while (*sp) {
if (f2 == 11) ut = atoll(sp);
if (f2 == 12) { st = atoll(sp); break; }
while (*sp && *sp != ' ') sp++;
while (*sp == ' ') sp++;
if (f2 == 12) {
st = atoll(sp);
break;
}
while (*sp && *sp != ' ')
sp++;
while (*sp == ' ')
sp++;
f2++;
}
}
@@ -156,15 +189,14 @@ int main(int argc, char **argv) {
long long vcs = -1;
snprintf(path, sizeof path, "/proc/%d/task/%d/sched", pid, t->tid);
if (readfile(path, buf, sizeof buf) > 0) {
char *v = strstr(buf, "nr_voluntary_switches");
char* v = strstr(buf, "nr_voluntary_switches");
if (v) {
char *col = strchr(v, ':');
char* col = strchr(v, ':');
if (col) vcs = atoll(col + 1);
}
}
fprintf(f, "%d,%s,%lld,%lld,%lld,%lld,\"", t->tid, t->name,
(ut + st) - t->cpu0, (vcs >= 0 && t->vcs0 >= 0) ? vcs - t->vcs0 : -1,
t->run, t->openerr);
fprintf(f, "%d,%s,%lld,%lld,%lld,%lld,\"", t->tid, t->name, (ut + st) - t->cpu0,
(vcs >= 0 && t->vcs0 >= 0) ? vcs - t->vcs0 : -1, t->run, t->openerr);
for (int s = 0; s < MAXSY; s++)
if (t->sys[s]) fprintf(f, "%d:%lld;", s, t->sys[s]);
fprintf(f, "\"\n");
@@ -18,53 +18,57 @@
#undef GL_GLEXT_PROTOTYPES
namespace MGITest {
class P5fPublicMipmapScenario : public ScenarioTest {
protected:
void Check(bool gpuWritten) {
if (!Ready()) return;
GLuint texture = 0, fbo = 0;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
std::array<GLubyte, 4 * 4 * 4> red{};
for (std::size_t i = 0; i < red.size(); i += 4) {
red[i] = 255;
red[i + 3] = 255;
class P5fPublicMipmapScenario : public ScenarioTest {
protected:
void Check(bool gpuWritten) {
if (!Ready()) return;
GLuint texture = 0, fbo = 0;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
std::array<GLubyte, 4 * 4 * 4> red{};
for (std::size_t i = 0; i < red.size(); i += 4) {
red[i] = 255;
red[i + 3] = 255;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, red.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
if (gpuWritten) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum{GL_FRAMEBUFFER_COMPLETE});
glDisable(GL_SCISSOR_TEST);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
glGenerateMipmap(GL_TEXTURE_2D);
for (GLint level = 0; level <= 2; ++level) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, level);
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum{GL_FRAMEBUFFER_COMPLETE}) << level;
std::array<GLubyte, 4> pixel{71, 72, 73, 74};
glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel.data());
EXPECT_EQ(pixel[0], gpuWritten ? 0 : 255) << "mip=" << level;
EXPECT_EQ(pixel[1], gpuWritten ? 255 : 0) << "mip=" << level;
EXPECT_EQ(pixel[2], 0) << "mip=" << level;
EXPECT_EQ(pixel[3], 255) << "mip=" << level;
}
EXPECT_EQ(glGetError(), GLenum{GL_NO_ERROR});
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &texture);
Gl().EndFrame();
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, red.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
if (gpuWritten) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum{GL_FRAMEBUFFER_COMPLETE});
glDisable(GL_SCISSOR_TEST);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
glGenerateMipmap(GL_TEXTURE_2D);
for (GLint level = 0; level <= 2; ++level) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, level);
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum{GL_FRAMEBUFFER_COMPLETE}) << level;
std::array<GLubyte, 4> pixel{71, 72, 73, 74};
glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel.data());
EXPECT_EQ(pixel[0], gpuWritten ? 0 : 255) << "mip=" << level;
EXPECT_EQ(pixel[1], gpuWritten ? 255 : 0) << "mip=" << level;
EXPECT_EQ(pixel[2], 0) << "mip=" << level;
EXPECT_EQ(pixel[3], 255) << "mip=" << level;
}
EXPECT_EQ(glGetError(), GLenum{GL_NO_ERROR});
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &texture);
Gl().EndFrame();
}
};
};
TEST_F(P5fPublicMipmapScenario, UploadedRgba8BaseGeneratesRedMipChain) { Check(false); }
TEST_F(P5fPublicMipmapScenario, GpuWrittenBaseGeneratesGreenMipChainWithoutStaleCpuUpload) { Check(true); }
TEST_F(P5fPublicMipmapScenario, UploadedRgba8BaseGeneratesRedMipChain) {
Check(false);
}
TEST_F(P5fPublicMipmapScenario, GpuWrittenBaseGeneratesGreenMipChainWithoutStaleCpuUpload) {
Check(true);
}
} // namespace MGITest
File diff suppressed because it is too large Load Diff
+27 -30
View File
@@ -33,31 +33,31 @@
namespace {
// The single fact that makes this spike conclusive rather than suggestive: the exec'd
// child reports the domain it is running in. `u:r:untrusted_app:s0:...` means an ordinary
// application process really did exec this file; anything else (shell, adb, a platform
// domain) means the test was run the wrong way and its verdict does not transfer.
void ReadSelinuxContext(char* out, size_t size) {
out[0] = '\0';
FILE* file = std::fopen("/proc/self/attr/current", "r");
if (file == nullptr) {
std::snprintf(out, size, "<unreadable>");
return;
}
const size_t read = std::fread(out, 1, size - 1, file);
std::fclose(file);
out[read] = '\0';
// The kernel returns the context NUL-terminated inside the read; trim anything after.
for (size_t index = 0; index < read; ++index) {
if (out[index] == '\n' || out[index] == '\0') {
out[index] = '\0';
break;
// The single fact that makes this spike conclusive rather than suggestive: the exec'd
// child reports the domain it is running in. `u:r:untrusted_app:s0:...` means an ordinary
// application process really did exec this file; anything else (shell, adb, a platform
// domain) means the test was run the wrong way and its verdict does not transfer.
void ReadSelinuxContext(char* out, size_t size) {
out[0] = '\0';
FILE* file = std::fopen("/proc/self/attr/current", "r");
if (file == nullptr) {
std::snprintf(out, size, "<unreadable>");
return;
}
const size_t read = std::fread(out, 1, size - 1, file);
std::fclose(file);
out[read] = '\0';
// The kernel returns the context NUL-terminated inside the read; trim anything after.
for (size_t index = 0; index < read; ++index) {
if (out[index] == '\n' || out[index] == '\0') {
out[index] = '\0';
break;
}
}
if (out[0] == '\0') {
std::snprintf(out, size, "<empty>");
}
}
if (out[0] == '\0') {
std::snprintf(out, size, "<empty>");
}
}
} // namespace
@@ -69,9 +69,8 @@ int main(int argc, char** argv) {
std::snprintf(line, sizeof(line),
"MobileGLServer-spike ok argv0=%s pid=%d ppid=%d uid=%d gid=%d argc=%d "
"selinux=%s\n",
argc > 0 && argv[0] != nullptr ? argv[0] : "<null>",
static_cast<int>(getpid()), static_cast<int>(getppid()),
static_cast<int>(getuid()), static_cast<int>(getgid()), argc, context);
argc > 0 && argv[0] != nullptr ? argv[0] : "<null>", static_cast<int>(getpid()),
static_cast<int>(getppid()), static_cast<int>(getuid()), static_cast<int>(getgid()), argc, context);
std::fputs(line, stdout);
std::fflush(stdout);
@@ -83,14 +82,12 @@ int main(int argc, char** argv) {
FILE* marker = std::fopen(argv[1], "w");
if (marker == nullptr) {
std::fprintf(stderr, "MobileGLServer-spike: cannot open marker %s: %s\n", argv[1],
std::strerror(errno));
std::fprintf(stderr, "MobileGLServer-spike: cannot open marker %s: %s\n", argv[1], std::strerror(errno));
return 3;
}
std::fputs(line, marker);
if (std::fclose(marker) != 0) {
std::fprintf(stderr, "MobileGLServer-spike: cannot write marker %s: %s\n", argv[1],
std::strerror(errno));
std::fprintf(stderr, "MobileGLServer-spike: cannot write marker %s: %s\n", argv[1], std::strerror(errno));
return 4;
}
return 0;
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -2,10 +2,10 @@
namespace mobilegl_trace_dump {
// Installs the opt-in framebuffer-attachment and named-2D-texture dump hooks. The respective
// environments are MOBILEGL_TRACE_DUMP_FBO_ATTACHMENTS and MOBILEGL_TRACE_DUMP_TEXTURE_2D.
// Safe and cheap to call on every makeCurrent: the environment is consulted once and the hook is
// installed at most once.
void InstallIfRequested();
// Installs the opt-in framebuffer-attachment and named-2D-texture dump hooks. The respective
// environments are MOBILEGL_TRACE_DUMP_FBO_ATTACHMENTS and MOBILEGL_TRACE_DUMP_TEXTURE_2D.
// Safe and cheap to call on every makeCurrent: the environment is consulted once and the hook is
// installed at most once.
void InstallIfRequested();
} // namespace mobilegl_trace_dump
+99 -99
View File
@@ -16,136 +16,136 @@
#include <cstring>
#include <dlfcn.h>
void *_libGlHandle = nullptr;
void* _libGlHandle = nullptr;
namespace {
void *LookupSymbol(const char *procName);
void* LookupSymbol(const char* procName);
void *GetMobileGlHandle() {
if (_libGlHandle != nullptr) {
void* GetMobileGlHandle() {
if (_libGlHandle != nullptr) {
return _libGlHandle;
}
const char* library = std::getenv("MOBILEGL_TRACE_LIBRARY");
if (library != nullptr && library[0] != '\0') {
_libGlHandle = dlopen(library, RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD);
if (_libGlHandle == nullptr) {
_libGlHandle = dlopen(library, RTLD_NOW | RTLD_GLOBAL);
}
}
if (_libGlHandle == nullptr) {
_libGlHandle = dlopen("libMobileGL.so", RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD);
}
if (_libGlHandle == nullptr) {
_libGlHandle = dlopen("libMobileGL.so", RTLD_NOW | RTLD_GLOBAL);
}
return _libGlHandle;
}
const char *library = std::getenv("MOBILEGL_TRACE_LIBRARY");
if (library != nullptr && library[0] != '\0') {
_libGlHandle = dlopen(library, RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD);
if (_libGlHandle == nullptr) {
_libGlHandle = dlopen(library, RTLD_NOW | RTLD_GLOBAL);
using GlReadBuffer = void (*)(GLenum mode);
using GlReadPixels = void (*)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
void* pixels);
using GlGetIntegerv = void (*)(GLenum pname, GLint* data);
using GlGetError = GLenum (*)();
GlReadBuffer gRealGlReadBuffer = nullptr;
GlReadPixels gRealGlReadPixels = nullptr;
GlGetIntegerv gRealGlGetIntegerv = nullptr;
GlGetError gRealGlGetError = nullptr;
int gSuppressInvalidEnumCount = 0;
void MobileGLTraceReadBuffer(GLenum mode) {
if (mode == GL_BACK) {
++gSuppressInvalidEnumCount;
return;
}
}
if (_libGlHandle == nullptr) {
_libGlHandle = dlopen("libMobileGL.so", RTLD_NOW | RTLD_GLOBAL | RTLD_NOLOAD);
}
if (_libGlHandle == nullptr) {
_libGlHandle = dlopen("libMobileGL.so", RTLD_NOW | RTLD_GLOBAL);
}
return _libGlHandle;
}
using GlReadBuffer = void (*)(GLenum mode);
using GlReadPixels = void (*)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
void *pixels);
using GlGetIntegerv = void (*)(GLenum pname, GLint *data);
using GlGetError = GLenum (*)();
GlReadBuffer gRealGlReadBuffer = nullptr;
GlReadPixels gRealGlReadPixels = nullptr;
GlGetIntegerv gRealGlGetIntegerv = nullptr;
GlGetError gRealGlGetError = nullptr;
int gSuppressInvalidEnumCount = 0;
void MobileGLTraceReadBuffer(GLenum mode) {
if (mode == GL_BACK) {
++gSuppressInvalidEnumCount;
return;
}
if (gRealGlReadBuffer == nullptr) {
gRealGlReadBuffer = reinterpret_cast<GlReadBuffer>(LookupSymbol("glReadBuffer"));
}
if (gRealGlReadBuffer != nullptr) {
gRealGlReadBuffer(mode);
}
}
void MobileGLTraceReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
void *pixels) {
if (gRealGlReadPixels == nullptr) {
gRealGlReadPixels = reinterpret_cast<GlReadPixels>(LookupSymbol("glReadPixels"));
}
if (gRealGlReadPixels != nullptr) {
gRealGlReadPixels(x, y, width, height, format, type, pixels);
}
}
void MobileGLTraceGetIntegerv(GLenum pname, GLint *data) {
if (pname == GL_READ_BUFFER) {
if (data != nullptr) {
*data = GL_BACK;
if (gRealGlReadBuffer == nullptr) {
gRealGlReadBuffer = reinterpret_cast<GlReadBuffer>(LookupSymbol("glReadBuffer"));
}
++gSuppressInvalidEnumCount;
return;
}
if (gRealGlGetIntegerv == nullptr) {
gRealGlGetIntegerv = reinterpret_cast<GlGetIntegerv>(LookupSymbol("glGetIntegerv"));
}
if (gRealGlGetIntegerv != nullptr) {
gRealGlGetIntegerv(pname, data);
}
}
GLenum MobileGLTraceGetError() {
if (gRealGlGetError == nullptr) {
gRealGlGetError = reinterpret_cast<GlGetError>(LookupSymbol("glGetError"));
}
if (gRealGlGetError == nullptr) {
return GL_NO_ERROR;
}
GLenum error = gRealGlGetError();
if (error == GL_INVALID_ENUM && gSuppressInvalidEnumCount > 0) {
--gSuppressInvalidEnumCount;
return GL_NO_ERROR;
}
return error;
}
void *LookupSymbol(const char *procName) {
void *mobileGl = GetMobileGlHandle();
if (mobileGl != nullptr) {
void *proc = dlsym(mobileGl, procName);
if (proc != nullptr) {
return proc;
if (gRealGlReadBuffer != nullptr) {
gRealGlReadBuffer(mode);
}
}
return dlsym(RTLD_DEFAULT, procName);
}
void MobileGLTraceReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type,
void* pixels) {
if (gRealGlReadPixels == nullptr) {
gRealGlReadPixels = reinterpret_cast<GlReadPixels>(LookupSymbol("glReadPixels"));
}
if (gRealGlReadPixels != nullptr) {
gRealGlReadPixels(x, y, width, height, format, type, pixels);
}
}
void MobileGLTraceGetIntegerv(GLenum pname, GLint* data) {
if (pname == GL_READ_BUFFER) {
if (data != nullptr) {
*data = GL_BACK;
}
++gSuppressInvalidEnumCount;
return;
}
if (gRealGlGetIntegerv == nullptr) {
gRealGlGetIntegerv = reinterpret_cast<GlGetIntegerv>(LookupSymbol("glGetIntegerv"));
}
if (gRealGlGetIntegerv != nullptr) {
gRealGlGetIntegerv(pname, data);
}
}
GLenum MobileGLTraceGetError() {
if (gRealGlGetError == nullptr) {
gRealGlGetError = reinterpret_cast<GlGetError>(LookupSymbol("glGetError"));
}
if (gRealGlGetError == nullptr) {
return GL_NO_ERROR;
}
GLenum error = gRealGlGetError();
if (error == GL_INVALID_ENUM && gSuppressInvalidEnumCount > 0) {
--gSuppressInvalidEnumCount;
return GL_NO_ERROR;
}
return error;
}
void* LookupSymbol(const char* procName) {
void* mobileGl = GetMobileGlHandle();
if (mobileGl != nullptr) {
void* proc = dlsym(mobileGl, procName);
if (proc != nullptr) {
return proc;
}
}
return dlsym(RTLD_DEFAULT, procName);
}
} // namespace
void *_getPublicProcAddress(const char *procName) {
void* _getPublicProcAddress(const char* procName) {
if (strcmp(procName, "glReadPixels") == 0) {
return reinterpret_cast<void *>(&MobileGLTraceReadPixels);
return reinterpret_cast<void*>(&MobileGLTraceReadPixels);
}
if (strcmp(procName, "glReadBuffer") == 0) {
return reinterpret_cast<void *>(&MobileGLTraceReadBuffer);
return reinterpret_cast<void*>(&MobileGLTraceReadBuffer);
}
if (strcmp(procName, "glGetIntegerv") == 0) {
return reinterpret_cast<void *>(&MobileGLTraceGetIntegerv);
return reinterpret_cast<void*>(&MobileGLTraceGetIntegerv);
}
if (strcmp(procName, "glGetError") == 0) {
return reinterpret_cast<void *>(&MobileGLTraceGetError);
return reinterpret_cast<void*>(&MobileGLTraceGetError);
}
return LookupSymbol(procName);
}
void *_getPrivateProcAddress(const char *procName) {
void *proc = LookupSymbol(procName);
void* _getPrivateProcAddress(const char* procName) {
void* proc = LookupSymbol(procName);
if (proc != nullptr) {
return proc;
}
if (_eglGetProcAddress != nullptr) {
return reinterpret_cast<void *>(_eglGetProcAddress(procName));
return reinterpret_cast<void*>(_eglGetProcAddress(procName));
}
return nullptr;
File diff suppressed because it is too large Load Diff
@@ -46,383 +46,401 @@
namespace {
constexpr unsigned kUniformBuffer = 0x8A11;
constexpr unsigned kShaderStorageBuffer = 0x90D2;
constexpr unsigned kDynamicStorageBit = 0x0100;
constexpr unsigned kRelocBufferName = 1000000;
constexpr unsigned kUniformBuffer = 0x8A11;
constexpr unsigned kShaderStorageBuffer = 0x90D2;
constexpr unsigned kDynamicStorageBit = 0x0100;
constexpr unsigned kRelocBufferName = 1000000;
[[noreturn]] void Fail(const trace::Call *call, const std::string &what) {
std::fprintf(stderr, "error: call %u %s: %s\n", call ? call->no : 0u, call ? call->name() : "-", what.c_str());
std::exit(1);
}
bool Is(const trace::Call *call, const char *name) { return std::strcmp(call->name(), name) == 0; }
bool StartsWith(const char *s, const char *prefix) { return std::strncmp(s, prefix, std::strlen(prefix)) == 0; }
bool ReadsIndexedBuffers(const trace::Call *call) {
const char *n = call->name();
return StartsWith(n, "glDraw") || StartsWith(n, "glMultiDraw") || StartsWith(n, "glDispatchCompute");
}
bool IsRangeTarget(unsigned target) { return target == kUniformBuffer || target == kShaderStorageBuffer; }
struct MapRegion {
unsigned buffer;
uint64_t offset;
uint64_t length;
uint64_t ptr;
};
struct Reloc {
unsigned src = 0;
uint64_t offset = 0;
uint64_t size = 0;
bool dirty = true;
std::vector<char> last;
const trace::EnumSig *targetSig = nullptr;
};
// Signatures for the calls this tool inserts; ids are allocated past the trace's own.
struct Sigs {
const char *createNames[2] = {"n", "buffers"};
const char *storageNames[4] = {"buffer", "size", "data", "flags"};
const char *subDataNames[4] = {"buffer", "offset", "size", "data"};
const char *bindRangeNames[5] = {"target", "index", "buffer", "offset", "size"};
const char *bindNames[2] = {"target", "buffer"};
trace::FunctionSig create{}, storage{}, subData{}, bindRange{}, bind{};
void Init(trace::Id firstId) {
create = {firstId + 0, "glCreateBuffers", 2, createNames};
storage = {firstId + 1, "glNamedBufferStorage", 4, storageNames};
subData = {firstId + 2, "glNamedBufferSubData", 4, subDataNames};
bindRange = {firstId + 3, "glBindBufferRange", 5, bindRangeNames};
bind = {firstId + 4, "glBindBuffer", 2, bindNames};
}
};
class Rewriter {
public:
uint64_t align = 1024;
std::set<unsigned> mapCalls;
int Run(const char *in, const char *out) {
Scan(in);
if (relocSrc_.empty()) {
std::fprintf(stderr, "nothing to realign at %llu bytes\n", (unsigned long long)align);
return 2;
}
sigs_.Init(maxSigId_ + 1);
stride_ = (maxRelocSize_ + align - 1) / align * align;
Pass(in, nullptr); // dry run: count the slots
slotCap_ = slotsUsed_;
Pass(in, out);
std::fprintf(stderr,
"realigned %llu binds of %zu source buffer(s) to %llu bytes: %llu uploads, relocation buffer %llu "
"bytes, %llu calls inserted\n",
(unsigned long long)relocBinds_, relocSrc_.size(), (unsigned long long)align,
(unsigned long long)slotsUsed_, (unsigned long long)(slotCap_ * stride_),
(unsigned long long)inserted_);
for (const auto &m : callMap_) {
std::printf("call %u -> %llu\n", m.first, (unsigned long long)m.second);
}
return 0;
[[noreturn]] void Fail(const trace::Call* call, const std::string& what) {
std::fprintf(stderr, "error: call %u %s: %s\n", call ? call->no : 0u, call ? call->name() : "-", what.c_str());
std::exit(1);
}
private:
// Pass 0: which buffers need relocating, and the sizes involved.
void Scan(const char *in) {
trace::Parser p;
if (!p.open(in)) Fail(nullptr, std::string("cannot open ") + in);
trace::Call *call;
while ((call = p.parse_call())) {
maxSigId_ = std::max(maxSigId_, call->sig->id);
if (Is(call, "glBindBufferRange")) {
unsigned target = unsigned(call->arg(0).toSInt());
uint64_t offset = uint64_t(call->arg(3).toSInt());
if (IsRangeTarget(target) && offset % align != 0) {
relocSrc_.insert(unsigned(call->arg(2).toUInt()));
maxRelocSize_ = std::max(maxRelocSize_, uint64_t(call->arg(4).toSInt()));
}
} else if (Is(call, "glBindBuffersRange")) {
unsigned target = unsigned(call->arg(0).toSInt());
const trace::Array *offsets = call->arg(4).toArray();
if (IsRangeTarget(target) && offsets) {
for (const trace::Value *v : offsets->values) {
if (uint64_t(v->toSInt()) % align != 0) Fail(call, "misaligned glBindBuffersRange is not handled");
bool Is(const trace::Call* call, const char* name) {
return std::strcmp(call->name(), name) == 0;
}
bool StartsWith(const char* s, const char* prefix) {
return std::strncmp(s, prefix, std::strlen(prefix)) == 0;
}
bool ReadsIndexedBuffers(const trace::Call* call) {
const char* n = call->name();
return StartsWith(n, "glDraw") || StartsWith(n, "glMultiDraw") || StartsWith(n, "glDispatchCompute");
}
bool IsRangeTarget(unsigned target) {
return target == kUniformBuffer || target == kShaderStorageBuffer;
}
struct MapRegion {
unsigned buffer;
uint64_t offset;
uint64_t length;
uint64_t ptr;
};
struct Reloc {
unsigned src = 0;
uint64_t offset = 0;
uint64_t size = 0;
bool dirty = true;
std::vector<char> last;
const trace::EnumSig* targetSig = nullptr;
};
// Signatures for the calls this tool inserts; ids are allocated past the trace's own.
struct Sigs {
const char* createNames[2] = {"n", "buffers"};
const char* storageNames[4] = {"buffer", "size", "data", "flags"};
const char* subDataNames[4] = {"buffer", "offset", "size", "data"};
const char* bindRangeNames[5] = {"target", "index", "buffer", "offset", "size"};
const char* bindNames[2] = {"target", "buffer"};
trace::FunctionSig create{}, storage{}, subData{}, bindRange{}, bind{};
void Init(trace::Id firstId) {
create = {firstId + 0, "glCreateBuffers", 2, createNames};
storage = {firstId + 1, "glNamedBufferStorage", 4, storageNames};
subData = {firstId + 2, "glNamedBufferSubData", 4, subDataNames};
bindRange = {firstId + 3, "glBindBufferRange", 5, bindRangeNames};
bind = {firstId + 4, "glBindBuffer", 2, bindNames};
}
};
class Rewriter {
public:
uint64_t align = 1024;
std::set<unsigned> mapCalls;
int Run(const char* in, const char* out) {
Scan(in);
if (relocSrc_.empty()) {
std::fprintf(stderr, "nothing to realign at %llu bytes\n", (unsigned long long)align);
return 2;
}
sigs_.Init(maxSigId_ + 1);
stride_ = (maxRelocSize_ + align - 1) / align * align;
Pass(in, nullptr); // dry run: count the slots
slotCap_ = slotsUsed_;
Pass(in, out);
std::fprintf(
stderr,
"realigned %llu binds of %zu source buffer(s) to %llu bytes: %llu uploads, relocation buffer %llu "
"bytes, %llu calls inserted\n",
(unsigned long long)relocBinds_, relocSrc_.size(), (unsigned long long)align,
(unsigned long long)slotsUsed_, (unsigned long long)(slotCap_ * stride_),
(unsigned long long)inserted_);
for (const auto& m : callMap_) {
std::printf("call %u -> %llu\n", m.first, (unsigned long long)m.second);
}
return 0;
}
private:
// Pass 0: which buffers need relocating, and the sizes involved.
void Scan(const char* in) {
trace::Parser p;
if (!p.open(in)) Fail(nullptr, std::string("cannot open ") + in);
trace::Call* call;
while ((call = p.parse_call())) {
maxSigId_ = std::max(maxSigId_, call->sig->id);
if (Is(call, "glBindBufferRange")) {
unsigned target = unsigned(call->arg(0).toSInt());
uint64_t offset = uint64_t(call->arg(3).toSInt());
if (IsRangeTarget(target) && offset % align != 0) {
relocSrc_.insert(unsigned(call->arg(2).toUInt()));
maxRelocSize_ = std::max(maxRelocSize_, uint64_t(call->arg(4).toSInt()));
}
} else if (Is(call, "glBindBuffersRange")) {
unsigned target = unsigned(call->arg(0).toSInt());
const trace::Array* offsets = call->arg(4).toArray();
if (IsRangeTarget(target) && offsets) {
for (const trace::Value* v : offsets->values) {
if (uint64_t(v->toSInt()) % align != 0)
Fail(call, "misaligned glBindBuffersRange is not handled");
}
}
}
delete call;
}
delete call;
}
}
void Pass(const char *in, const char *out) {
trace::Parser p;
if (!p.open(in)) Fail(nullptr, std::string("cannot open ") + in);
trace::Writer writer;
writer_ = nullptr;
if (out) {
if (!writer.open(out, p.getVersion(), p.getProperties())) Fail(nullptr, std::string("cannot create ") + out);
writer_ = &writer;
void Pass(const char* in, const char* out) {
trace::Parser p;
if (!p.open(in)) Fail(nullptr, std::string("cannot open ") + in);
trace::Writer writer;
writer_ = nullptr;
if (out) {
if (!writer.open(out, p.getVersion(), p.getProperties()))
Fail(nullptr, std::string("cannot create ") + out);
writer_ = &writer;
}
shadow_.clear();
maps_.clear();
generic_.clear();
relocs_.clear();
slotsUsed_ = 0;
relocBinds_ = 0;
inserted_ = 0;
created_ = false;
trace::Call* call;
while ((call = p.parse_call())) {
Process(call);
delete call;
}
if (writer_) writer.close();
writer_ = nullptr;
}
shadow_.clear();
maps_.clear();
generic_.clear();
relocs_.clear();
slotsUsed_ = 0;
relocBinds_ = 0;
inserted_ = 0;
created_ = false;
trace::Call *call;
while ((call = p.parse_call())) {
Process(call);
delete call;
unsigned BufferArg(trace::Call* call, unsigned index) { return unsigned(call->arg(index).toUInt()); }
unsigned Generic(unsigned target) {
auto it = generic_.find(target);
return it == generic_.end() ? 0 : it->second;
}
if (writer_) writer.close();
writer_ = nullptr;
}
unsigned BufferArg(trace::Call *call, unsigned index) { return unsigned(call->arg(index).toUInt()); }
unsigned Generic(unsigned target) {
auto it = generic_.find(target);
return it == generic_.end() ? 0 : it->second;
}
std::vector<char> *Shadow(unsigned buffer) {
auto it = shadow_.find(buffer);
return it == shadow_.end() ? nullptr : &it->second;
}
void Specify(trace::Call *call, unsigned buffer, uint64_t size, trace::Value *data) {
if (!relocSrc_.count(buffer)) return;
std::vector<char> &s = shadow_[buffer];
s.assign(size, 0);
const trace::Blob *blob = data ? data->toBlob() : nullptr;
if (blob) std::memcpy(s.data(), blob->buf, std::min<uint64_t>(size, blob->size));
for (auto &r : relocs_) {
if (r.second.src == buffer) r.second.dirty = true;
std::vector<char>* Shadow(unsigned buffer) {
auto it = shadow_.find(buffer);
return it == shadow_.end() ? nullptr : &it->second;
}
(void)call;
}
void Write(trace::Call *call, unsigned buffer, uint64_t offset, const char *bytes, uint64_t size) {
std::vector<char> *s = Shadow(buffer);
if (!s) return;
if (offset + size > s->size()) Fail(call, "write past the end of a tracked buffer");
std::memcpy(s->data() + offset, bytes, size);
}
void Specify(trace::Call* call, unsigned buffer, uint64_t size, trace::Value* data) {
if (!relocSrc_.count(buffer)) return;
std::vector<char>& s = shadow_[buffer];
s.assign(size, 0);
const trace::Blob* blob = data ? data->toBlob() : nullptr;
if (blob) std::memcpy(s.data(), blob->buf, std::min<uint64_t>(size, blob->size));
for (auto& r : relocs_) {
if (r.second.src == buffer) r.second.dirty = true;
}
(void)call;
}
void Refuse(trace::Call *call, unsigned buffer) {
if (relocSrc_.count(buffer)) Fail(call, "writes a relocated source buffer in a way the trace does not carry the bytes of");
}
void Write(trace::Call* call, unsigned buffer, uint64_t offset, const char* bytes, uint64_t size) {
std::vector<char>* s = Shadow(buffer);
if (!s) return;
if (offset + size > s->size()) Fail(call, "write past the end of a tracked buffer");
std::memcpy(s->data() + offset, bytes, size);
}
void Emit(trace::Call *call) {
if (!writer_) return;
if (mapCalls.count(call->no)) callMap_[call->no] = writtenCalls_;
writer_->writeCall(call);
++writtenCalls_;
}
void Refuse(trace::Call* call, unsigned buffer) {
if (relocSrc_.count(buffer))
Fail(call, "writes a relocated source buffer in a way the trace does not carry the bytes of");
}
void EmitNew(trace::Call *call) {
++inserted_;
if (writer_) {
void Emit(trace::Call* call) {
if (!writer_) return;
if (mapCalls.count(call->no)) callMap_[call->no] = writtenCalls_;
writer_->writeCall(call);
++writtenCalls_;
}
delete call;
}
trace::Call *NewCall(const trace::FunctionSig *sig, unsigned thread) {
return new trace::Call(sig, trace::CallFlags(0), thread);
}
void EmitBind(unsigned thread, const trace::EnumSig *targetSig, unsigned target, unsigned buffer) {
trace::Call *c = NewCall(&sigs_.bind, thread);
c->args[0].value = targetSig ? static_cast<trace::Value *>(new trace::Enum(targetSig, target))
: static_cast<trace::Value *>(new trace::SInt(target));
c->args[1].value = new trace::UInt(buffer);
EmitNew(c);
}
void CreateRelocBuffer(unsigned thread) {
trace::Call *c = NewCall(&sigs_.create, thread);
c->args[0].value = new trace::SInt(1);
trace::Array *names = new trace::Array(1);
names->values[0] = new trace::UInt(kRelocBufferName);
c->args[1].value = names;
EmitNew(c);
c = NewCall(&sigs_.storage, thread);
c->args[0].value = new trace::UInt(kRelocBufferName);
c->args[1].value = new trace::SInt(int64_t(slotCap_ * stride_));
c->args[2].value = new trace::Null();
c->args[3].value = new trace::UInt(kDynamicStorageBit);
EmitNew(c);
created_ = true;
}
void Upload(trace::Call *before, unsigned target, unsigned index, Reloc &r) {
std::vector<char> *s = Shadow(r.src);
if (!s) Fail(before, "a relocated range reads a buffer whose storage the trace never specified");
if (r.offset + r.size > s->size()) Fail(before, "a relocated range runs past its buffer");
std::vector<char> bytes(s->begin() + r.offset, s->begin() + r.offset + r.size);
if (!r.dirty && bytes == r.last) return;
unsigned thread = before->thread_id;
if (writer_ && slotsUsed_ >= slotCap_) Fail(before, "relocation slots exceeded the dry run");
uint64_t slotOffset = slotsUsed_++ * stride_;
if (!created_) CreateRelocBuffer(thread);
trace::Call *c = NewCall(&sigs_.subData, thread);
c->args[0].value = new trace::UInt(kRelocBufferName);
c->args[1].value = new trace::SInt(int64_t(slotOffset));
c->args[2].value = new trace::SInt(int64_t(r.size));
trace::Blob *blob = new trace::Blob(r.size);
std::memcpy(blob->buf, bytes.data(), r.size);
c->args[3].value = blob;
EmitNew(c);
c = NewCall(&sigs_.bindRange, thread);
c->args[0].value = r.targetSig ? static_cast<trace::Value *>(new trace::Enum(r.targetSig, target))
: static_cast<trace::Value *>(new trace::SInt(target));
c->args[1].value = new trace::UInt(index);
c->args[2].value = new trace::UInt(kRelocBufferName);
c->args[3].value = new trace::SInt(int64_t(slotOffset));
c->args[4].value = new trace::SInt(int64_t(r.size));
EmitNew(c);
// glBindBufferRange also moved the generic binding; put back the app's.
EmitBind(thread, r.targetSig, target, Generic(target));
r.last = std::move(bytes);
r.dirty = false;
}
void Process(trace::Call *call) {
const char *n = call->name();
if (Is(call, "glBindBuffer")) {
generic_[unsigned(call->arg(0).toSInt())] = BufferArg(call, 1);
} else if (Is(call, "glBindBufferBase")) {
unsigned target = unsigned(call->arg(0).toSInt());
generic_[target] = BufferArg(call, 2);
relocs_.erase({target, unsigned(call->arg(1).toUInt())});
} else if (Is(call, "glBindBufferRange")) {
unsigned target = unsigned(call->arg(0).toSInt());
unsigned index = unsigned(call->arg(1).toUInt());
unsigned buffer = BufferArg(call, 2);
uint64_t offset = uint64_t(call->arg(3).toSInt());
generic_[target] = buffer;
if (IsRangeTarget(target) && offset % align != 0) {
Reloc r;
r.src = buffer;
r.offset = offset;
r.size = uint64_t(call->arg(4).toSInt());
trace::Value &t = call->arg(0);
trace::Enum *e = nullptr;
// Enum values print by name in dumps; keep the trace's own enum signature when it has one.
struct EnumProbe : trace::Visitor {
trace::Enum *found = nullptr;
void visit(trace::Enum *node) override { found = node; }
} probe;
t.visit(probe);
e = probe.found;
r.targetSig = e ? e->sig : nullptr;
relocs_[{target, index}] = std::move(r);
++relocBinds_;
if (mapCalls.count(call->no) && writer_) callMap_[call->no] = writtenCalls_;
// Only the generic binding survives at this point; the indexed one is bound before its first read.
EmitBind(call->thread_id, e ? e->sig : nullptr, target, buffer);
--inserted_; // a replacement, not an insertion
return;
void EmitNew(trace::Call* call) {
++inserted_;
if (writer_) {
writer_->writeCall(call);
++writtenCalls_;
}
relocs_.erase({target, index});
} else if (Is(call, "glBindBuffersBase") || Is(call, "glBindBuffersRange")) {
unsigned target = unsigned(call->arg(0).toSInt());
unsigned first = unsigned(call->arg(1).toUInt());
unsigned count = unsigned(call->arg(2).toSInt());
for (unsigned i = first; i < first + count; ++i) relocs_.erase({target, i});
} else if (Is(call, "glNamedBufferStorage") || Is(call, "glNamedBufferData")) {
Specify(call, BufferArg(call, 0), uint64_t(call->arg(1).toSInt()), &call->arg(2));
} else if (Is(call, "glBufferStorage") || Is(call, "glBufferData")) {
Specify(call, Generic(unsigned(call->arg(0).toSInt())), uint64_t(call->arg(1).toSInt()), &call->arg(2));
} else if (Is(call, "glNamedBufferSubData") || Is(call, "glBufferSubData")) {
unsigned buffer = Is(call, "glNamedBufferSubData") ? BufferArg(call, 0) : Generic(unsigned(call->arg(0).toSInt()));
if (relocSrc_.count(buffer)) {
const trace::Blob *blob = call->arg(3).toBlob();
if (!blob) Fail(call, "subdata without bytes");
Write(call, buffer, uint64_t(call->arg(1).toSInt()), blob->buf,
std::min<uint64_t>(uint64_t(call->arg(2).toSInt()), blob->size));
}
} else if (Is(call, "glCopyNamedBufferSubData")) {
Refuse(call, BufferArg(call, 1));
} else if (Is(call, "glCopyBufferSubData")) {
Refuse(call, Generic(unsigned(call->arg(1).toSInt())));
} else if (StartsWith(n, "glClearNamedBuffer")) {
Refuse(call, BufferArg(call, 0));
} else if (StartsWith(n, "glClearBuffer") && (StartsWith(n, "glClearBufferData") || StartsWith(n, "glClearBufferSubData"))) {
Refuse(call, Generic(unsigned(call->arg(0).toSInt())));
} else if (Is(call, "glMapNamedBufferRange") || Is(call, "glMapBufferRange")) {
unsigned buffer = Is(call, "glMapNamedBufferRange") ? BufferArg(call, 0) : Generic(unsigned(call->arg(0).toSInt()));
if (relocSrc_.count(buffer) && call->ret) {
maps_.push_back({buffer, uint64_t(call->arg(1).toSInt()), uint64_t(call->arg(2).toSInt()),
call->ret->toUIntPtr()});
}
} else if (Is(call, "glMapNamedBuffer") || Is(call, "glMapBuffer")) {
unsigned buffer = Is(call, "glMapNamedBuffer") ? BufferArg(call, 0) : Generic(unsigned(call->arg(0).toSInt()));
std::vector<char> *s = Shadow(buffer);
if (s && call->ret) maps_.push_back({buffer, 0, s->size(), call->ret->toUIntPtr()});
} else if (Is(call, "glUnmapNamedBuffer") || Is(call, "glUnmapBuffer")) {
unsigned buffer = Is(call, "glUnmapNamedBuffer") ? BufferArg(call, 0) : Generic(unsigned(call->arg(0).toSInt()));
maps_.erase(std::remove_if(maps_.begin(), maps_.end(), [&](const MapRegion &m) { return m.buffer == buffer; }),
maps_.end());
} else if (Is(call, "memcpy")) {
uint64_t dest = call->arg(0).toUIntPtr();
const trace::Blob *blob = call->arg(1).toBlob();
uint64_t size = call->arg(2).toUInt();
if (blob) {
size = std::min<uint64_t>(size, blob->size);
for (const MapRegion &m : maps_) {
uint64_t lo = std::max(dest, m.ptr);
uint64_t hi = std::min(dest + size, m.ptr + m.length);
if (lo >= hi) continue;
Write(call, m.buffer, m.offset + (lo - m.ptr), blob->buf + (lo - dest), hi - lo);
}
}
} else if (ReadsIndexedBuffers(call)) {
for (auto &r : relocs_) Upload(call, r.first.first, r.first.second, r.second);
delete call;
}
Emit(call);
}
trace::Call* NewCall(const trace::FunctionSig* sig, unsigned thread) {
return new trace::Call(sig, trace::CallFlags(0), thread);
}
Sigs sigs_;
trace::Id maxSigId_ = 0;
std::set<unsigned> relocSrc_;
uint64_t maxRelocSize_ = 0;
uint64_t stride_ = 0;
uint64_t slotCap_ = 0;
uint64_t slotsUsed_ = 0;
uint64_t relocBinds_ = 0;
uint64_t inserted_ = 0;
uint64_t writtenCalls_ = 0;
bool created_ = false;
trace::Writer *writer_ = nullptr;
std::unordered_map<unsigned, std::vector<char>> shadow_;
std::vector<MapRegion> maps_;
std::unordered_map<unsigned, unsigned> generic_;
std::map<std::pair<unsigned, unsigned>, Reloc> relocs_;
std::map<unsigned, uint64_t> callMap_;
};
void EmitBind(unsigned thread, const trace::EnumSig* targetSig, unsigned target, unsigned buffer) {
trace::Call* c = NewCall(&sigs_.bind, thread);
c->args[0].value = targetSig ? static_cast<trace::Value*>(new trace::Enum(targetSig, target))
: static_cast<trace::Value*>(new trace::SInt(target));
c->args[1].value = new trace::UInt(buffer);
EmitNew(c);
}
} // namespace
void CreateRelocBuffer(unsigned thread) {
trace::Call* c = NewCall(&sigs_.create, thread);
c->args[0].value = new trace::SInt(1);
trace::Array* names = new trace::Array(1);
names->values[0] = new trace::UInt(kRelocBufferName);
c->args[1].value = names;
EmitNew(c);
int main(int argc, char **argv) {
c = NewCall(&sigs_.storage, thread);
c->args[0].value = new trace::UInt(kRelocBufferName);
c->args[1].value = new trace::SInt(int64_t(slotCap_ * stride_));
c->args[2].value = new trace::Null();
c->args[3].value = new trace::UInt(kDynamicStorageBit);
EmitNew(c);
created_ = true;
}
void Upload(trace::Call* before, unsigned target, unsigned index, Reloc& r) {
std::vector<char>* s = Shadow(r.src);
if (!s) Fail(before, "a relocated range reads a buffer whose storage the trace never specified");
if (r.offset + r.size > s->size()) Fail(before, "a relocated range runs past its buffer");
std::vector<char> bytes(s->begin() + r.offset, s->begin() + r.offset + r.size);
if (!r.dirty && bytes == r.last) return;
unsigned thread = before->thread_id;
if (writer_ && slotsUsed_ >= slotCap_) Fail(before, "relocation slots exceeded the dry run");
uint64_t slotOffset = slotsUsed_++ * stride_;
if (!created_) CreateRelocBuffer(thread);
trace::Call* c = NewCall(&sigs_.subData, thread);
c->args[0].value = new trace::UInt(kRelocBufferName);
c->args[1].value = new trace::SInt(int64_t(slotOffset));
c->args[2].value = new trace::SInt(int64_t(r.size));
trace::Blob* blob = new trace::Blob(r.size);
std::memcpy(blob->buf, bytes.data(), r.size);
c->args[3].value = blob;
EmitNew(c);
c = NewCall(&sigs_.bindRange, thread);
c->args[0].value = r.targetSig ? static_cast<trace::Value*>(new trace::Enum(r.targetSig, target))
: static_cast<trace::Value*>(new trace::SInt(target));
c->args[1].value = new trace::UInt(index);
c->args[2].value = new trace::UInt(kRelocBufferName);
c->args[3].value = new trace::SInt(int64_t(slotOffset));
c->args[4].value = new trace::SInt(int64_t(r.size));
EmitNew(c);
// glBindBufferRange also moved the generic binding; put back the app's.
EmitBind(thread, r.targetSig, target, Generic(target));
r.last = std::move(bytes);
r.dirty = false;
}
void Process(trace::Call* call) {
const char* n = call->name();
if (Is(call, "glBindBuffer")) {
generic_[unsigned(call->arg(0).toSInt())] = BufferArg(call, 1);
} else if (Is(call, "glBindBufferBase")) {
unsigned target = unsigned(call->arg(0).toSInt());
generic_[target] = BufferArg(call, 2);
relocs_.erase({target, unsigned(call->arg(1).toUInt())});
} else if (Is(call, "glBindBufferRange")) {
unsigned target = unsigned(call->arg(0).toSInt());
unsigned index = unsigned(call->arg(1).toUInt());
unsigned buffer = BufferArg(call, 2);
uint64_t offset = uint64_t(call->arg(3).toSInt());
generic_[target] = buffer;
if (IsRangeTarget(target) && offset % align != 0) {
Reloc r;
r.src = buffer;
r.offset = offset;
r.size = uint64_t(call->arg(4).toSInt());
trace::Value& t = call->arg(0);
trace::Enum* e = nullptr;
// Enum values print by name in dumps; keep the trace's own enum signature when it has one.
struct EnumProbe : trace::Visitor {
trace::Enum* found = nullptr;
void visit(trace::Enum* node) override { found = node; }
} probe;
t.visit(probe);
e = probe.found;
r.targetSig = e ? e->sig : nullptr;
relocs_[{target, index}] = std::move(r);
++relocBinds_;
if (mapCalls.count(call->no) && writer_) callMap_[call->no] = writtenCalls_;
// Only the generic binding survives at this point; the indexed one is bound before its first read.
EmitBind(call->thread_id, e ? e->sig : nullptr, target, buffer);
--inserted_; // a replacement, not an insertion
return;
}
relocs_.erase({target, index});
} else if (Is(call, "glBindBuffersBase") || Is(call, "glBindBuffersRange")) {
unsigned target = unsigned(call->arg(0).toSInt());
unsigned first = unsigned(call->arg(1).toUInt());
unsigned count = unsigned(call->arg(2).toSInt());
for (unsigned i = first; i < first + count; ++i)
relocs_.erase({target, i});
} else if (Is(call, "glNamedBufferStorage") || Is(call, "glNamedBufferData")) {
Specify(call, BufferArg(call, 0), uint64_t(call->arg(1).toSInt()), &call->arg(2));
} else if (Is(call, "glBufferStorage") || Is(call, "glBufferData")) {
Specify(call, Generic(unsigned(call->arg(0).toSInt())), uint64_t(call->arg(1).toSInt()), &call->arg(2));
} else if (Is(call, "glNamedBufferSubData") || Is(call, "glBufferSubData")) {
unsigned buffer =
Is(call, "glNamedBufferSubData") ? BufferArg(call, 0) : Generic(unsigned(call->arg(0).toSInt()));
if (relocSrc_.count(buffer)) {
const trace::Blob* blob = call->arg(3).toBlob();
if (!blob) Fail(call, "subdata without bytes");
Write(call, buffer, uint64_t(call->arg(1).toSInt()), blob->buf,
std::min<uint64_t>(uint64_t(call->arg(2).toSInt()), blob->size));
}
} else if (Is(call, "glCopyNamedBufferSubData")) {
Refuse(call, BufferArg(call, 1));
} else if (Is(call, "glCopyBufferSubData")) {
Refuse(call, Generic(unsigned(call->arg(1).toSInt())));
} else if (StartsWith(n, "glClearNamedBuffer")) {
Refuse(call, BufferArg(call, 0));
} else if (StartsWith(n, "glClearBuffer") &&
(StartsWith(n, "glClearBufferData") || StartsWith(n, "glClearBufferSubData"))) {
Refuse(call, Generic(unsigned(call->arg(0).toSInt())));
} else if (Is(call, "glMapNamedBufferRange") || Is(call, "glMapBufferRange")) {
unsigned buffer =
Is(call, "glMapNamedBufferRange") ? BufferArg(call, 0) : Generic(unsigned(call->arg(0).toSInt()));
if (relocSrc_.count(buffer) && call->ret) {
maps_.push_back({buffer, uint64_t(call->arg(1).toSInt()), uint64_t(call->arg(2).toSInt()),
call->ret->toUIntPtr()});
}
} else if (Is(call, "glMapNamedBuffer") || Is(call, "glMapBuffer")) {
unsigned buffer =
Is(call, "glMapNamedBuffer") ? BufferArg(call, 0) : Generic(unsigned(call->arg(0).toSInt()));
std::vector<char>* s = Shadow(buffer);
if (s && call->ret) maps_.push_back({buffer, 0, s->size(), call->ret->toUIntPtr()});
} else if (Is(call, "glUnmapNamedBuffer") || Is(call, "glUnmapBuffer")) {
unsigned buffer =
Is(call, "glUnmapNamedBuffer") ? BufferArg(call, 0) : Generic(unsigned(call->arg(0).toSInt()));
maps_.erase(
std::remove_if(maps_.begin(), maps_.end(), [&](const MapRegion& m) { return m.buffer == buffer; }),
maps_.end());
} else if (Is(call, "memcpy")) {
uint64_t dest = call->arg(0).toUIntPtr();
const trace::Blob* blob = call->arg(1).toBlob();
uint64_t size = call->arg(2).toUInt();
if (blob) {
size = std::min<uint64_t>(size, blob->size);
for (const MapRegion& m : maps_) {
uint64_t lo = std::max(dest, m.ptr);
uint64_t hi = std::min(dest + size, m.ptr + m.length);
if (lo >= hi) continue;
Write(call, m.buffer, m.offset + (lo - m.ptr), blob->buf + (lo - dest), hi - lo);
}
}
} else if (ReadsIndexedBuffers(call)) {
for (auto& r : relocs_)
Upload(call, r.first.first, r.first.second, r.second);
}
Emit(call);
}
Sigs sigs_;
trace::Id maxSigId_ = 0;
std::set<unsigned> relocSrc_;
uint64_t maxRelocSize_ = 0;
uint64_t stride_ = 0;
uint64_t slotCap_ = 0;
uint64_t slotsUsed_ = 0;
uint64_t relocBinds_ = 0;
uint64_t inserted_ = 0;
uint64_t writtenCalls_ = 0;
bool created_ = false;
trace::Writer* writer_ = nullptr;
std::unordered_map<unsigned, std::vector<char>> shadow_;
std::vector<MapRegion> maps_;
std::unordered_map<unsigned, unsigned> generic_;
std::map<std::pair<unsigned, unsigned>, Reloc> relocs_;
std::map<unsigned, uint64_t> callMap_;
};
} // namespace
int main(int argc, char** argv) {
Rewriter rw;
std::vector<const char *> files;
std::vector<const char*> files;
for (int i = 1; i < argc; ++i) {
if (!std::strcmp(argv[i], "--align") && i + 1 < argc) {
rw.align = std::strtoull(argv[++i], nullptr, 0);
+30 -34
View File
@@ -26,50 +26,47 @@
namespace {
int gFailures = 0;
int gFailures = 0;
void ExpectSplit(const std::string& input, const std::vector<std::string>& expected) {
const std::vector<std::string> actual = mobilegl_trace::SplitSemicolonList(input);
if (actual == expected) {
return;
void ExpectSplit(const std::string& input, const std::vector<std::string>& expected) {
const std::vector<std::string> actual = mobilegl_trace::SplitSemicolonList(input);
if (actual == expected) {
return;
}
++gFailures;
std::cerr << "SplitSemicolonList(\"" << input << "\") gave " << actual.size() << " entries, expected "
<< expected.size() << ":";
for (const std::string& entry : actual) {
std::cerr << " [" << entry << "]";
}
std::cerr << "\n";
}
++gFailures;
std::cerr << "SplitSemicolonList(\"" << input << "\") gave " << actual.size()
<< " entries, expected " << expected.size() << ":";
for (const std::string& entry : actual) {
std::cerr << " [" << entry << "]";
}
std::cerr << "\n";
}
const char* ActionName(mobilegl_trace::EnvOverrideAction action) {
switch (action) {
const char* ActionName(mobilegl_trace::EnvOverrideAction action) {
switch (action) {
case mobilegl_trace::EnvOverrideAction::Ignore:
return "Ignore";
case mobilegl_trace::EnvOverrideAction::Set:
return "Set";
case mobilegl_trace::EnvOverrideAction::Unset:
return "Unset";
}
return "?";
}
return "?";
}
void ExpectParse(const std::string& entry,
mobilegl_trace::EnvOverrideAction expectedAction,
const std::string& expectedKey,
const std::string& expectedValue) {
std::string key = "<untouched>";
std::string value = "<untouched>";
const mobilegl_trace::EnvOverrideAction action =
mobilegl_trace::ParseEnvOverride(entry, &key, &value);
if (action == expectedAction && key == expectedKey && value == expectedValue) {
return;
void ExpectParse(const std::string& entry, mobilegl_trace::EnvOverrideAction expectedAction,
const std::string& expectedKey, const std::string& expectedValue) {
std::string key = "<untouched>";
std::string value = "<untouched>";
const mobilegl_trace::EnvOverrideAction action = mobilegl_trace::ParseEnvOverride(entry, &key, &value);
if (action == expectedAction && key == expectedKey && value == expectedValue) {
return;
}
++gFailures;
std::cerr << "ParseEnvOverride(\"" << entry << "\") gave " << ActionName(action) << " key=[" << key
<< "] value=[" << value << "], expected " << ActionName(expectedAction) << " key=[" << expectedKey
<< "] value=[" << expectedValue << "]\n";
}
++gFailures;
std::cerr << "ParseEnvOverride(\"" << entry << "\") gave " << ActionName(action) << " key=["
<< key << "] value=[" << value << "], expected " << ActionName(expectedAction)
<< " key=[" << expectedKey << "] value=[" << expectedValue << "]\n";
}
} // namespace
@@ -79,8 +76,7 @@ int main() {
// Splitting.
ExpectSplit("", {});
ExpectSplit("MOBILEGL_PIPE_PUSH=1", {"MOBILEGL_PIPE_PUSH=1"});
ExpectSplit("MOBILEGL_PIPE_PUSH=1;MOBILEGL_PIPE_VERIFY=1",
{"MOBILEGL_PIPE_PUSH=1", "MOBILEGL_PIPE_VERIFY=1"});
ExpectSplit("MOBILEGL_PIPE_PUSH=1;MOBILEGL_PIPE_VERIFY=1", {"MOBILEGL_PIPE_PUSH=1", "MOBILEGL_PIPE_VERIFY=1"});
// A trailing ';' is what a caller that joins a list gets for free, and an empty entry
// must not be turned into an unsetenv("") - the whole passthrough would then depend on
// how carefully the shell script trimmed its own string.
+188 -188
View File
@@ -17,206 +17,206 @@ extern "C" void mobilegl_trace_set_requested_size(int width, int height);
namespace {
void PrintUsage(const char *argv0) {
std::cerr
<< "Usage: " << argv0 << " --trace PATH --target-call N --output DIR [options]\n"
<< "\n"
<< "Options:\n"
<< " --golden PATH Golden PNG to compare against\n"
<< " --alternate-golden PATH Additional acceptable golden PNG\n"
<< " --diff PATH Difference PNG output path\n"
<< " --backend NAME DirectGLES or DirectVulkan (default: DirectGLES)\n"
<< " --mobilegl-library PATH libMobileGL.so path (default: libMobileGL.so)\n"
<< " --width N Replay surface width override\n"
<< " --height N Replay surface height override\n"
<< " --window-surface Replay to a native window surface\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"
<< " --crop-x N Compare crop x\n"
<< " --crop-y N Compare crop y\n"
<< " --crop-width N Compare crop width\n"
<< " --crop-height N Compare crop height\n"
<< " --coherent-as-flush Set MOBILEGL_COHERENT_AS_FLUSH=1 for the replay\n"
<< " --benchmark Frame-timing mode: replay the whole trace, record a\n"
<< " wall-clock timestamp at every frame boundary, and skip\n"
<< " the snapshot and the golden comparison. --target-call is\n"
<< " not required in this mode.\n"
<< " --benchmark-tail-frames=N Frames at the end of the run the mean/median/p95 are\n"
<< " computed over (default: 200, clamped to the frame count)\n"
<< " --benchmark-finish=0/1 glFinish at every frame boundary so a frame time covers\n"
<< " GPU completion and not just CPU submission (default: 1)\n"
<< " --benchmark-result=PATH Timing JSON output (default: OUTPUT/benchmark.json)\n"
<< " --dump-fbo-attachments CALL:DIR[:FBO,FBO,...]\n"
<< " At CALL, write every colour attachment and the depth\n"
<< " attachment of every live framebuffer object into DIR as\n"
<< " fbo<N>-att<M>.png / fbo<N>-depth.png, plus a manifest.txt\n"
<< " of formats and per-channel statistics. Repeatable.\n";
}
bool ReadValue(int argc, char **argv, int &index, std::string &out) {
if (index + 1 >= argc) {
std::cerr << "Missing value for " << argv[index] << "\n";
return false;
}
out = argv[++index];
return true;
}
bool ReadInt(int argc, char **argv, int &index, int &out) {
std::string value;
if (!ReadValue(argc, argv, index, value)) {
return false;
}
out = std::atoi(value.c_str());
return true;
}
bool ReadLongLong(int argc, char **argv, int &index, long long &out) {
std::string value;
if (!ReadValue(argc, argv, index, value)) {
return false;
}
out = std::strtoll(value.c_str(), nullptr, 10);
return true;
}
bool ReadDouble(int argc, char **argv, int &index, double &out) {
std::string value;
if (!ReadValue(argc, argv, index, value)) {
return false;
}
out = std::strtod(value.c_str(), nullptr);
return true;
}
// The benchmark options are documented as --name=VALUE. The space-separated spelling every
// other option here uses is accepted too, so --benchmark-tail-frames=300 and
// --benchmark-tail-frames 300 both work; hasInlineValue says which one was given.
bool MatchOption(const std::string &arg, const char *name, std::string &inlineValue, bool &hasInlineValue) {
if (arg == name) {
hasInlineValue = false;
return true;
}
const std::string prefix = std::string(name) + "=";
if (arg.compare(0, prefix.size(), prefix) == 0) {
inlineValue = arg.substr(prefix.size());
hasInlineValue = true;
return true;
}
return false;
}
bool ParseArgs(int argc, char **argv, mobilegl_trace::Request &request) {
request.backend = "DirectGLES";
// MOBILEGL_ITRP_FIX is a tri-state (Auto/ForceOn/ForceOff); the request only carries ForceOn.
if (const char *itrpFix = std::getenv("MOBILEGL_ITRP_FIX")) {
std::string value = itrpFix;
for (char &ch : value) ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
request.iterationRPFix = value == "1" || value == "true" || value == "on" || value == "forceon";
void PrintUsage(const char* argv0) {
std::cerr << "Usage: " << argv0 << " --trace PATH --target-call N --output DIR [options]\n"
<< "\n"
<< "Options:\n"
<< " --golden PATH Golden PNG to compare against\n"
<< " --alternate-golden PATH Additional acceptable golden PNG\n"
<< " --diff PATH Difference PNG output path\n"
<< " --backend NAME DirectGLES or DirectVulkan (default: DirectGLES)\n"
<< " --mobilegl-library PATH libMobileGL.so path (default: libMobileGL.so)\n"
<< " --width N Replay surface width override\n"
<< " --height N Replay surface height override\n"
<< " --window-surface Replay to a native window surface\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"
<< " --crop-x N Compare crop x\n"
<< " --crop-y N Compare crop y\n"
<< " --crop-width N Compare crop width\n"
<< " --crop-height N Compare crop height\n"
<< " --coherent-as-flush Set MOBILEGL_COHERENT_AS_FLUSH=1 for the replay\n"
<< " --benchmark Frame-timing mode: replay the whole trace, record a\n"
<< " wall-clock timestamp at every frame boundary, and skip\n"
<< " the snapshot and the golden comparison. --target-call is\n"
<< " not required in this mode.\n"
<< " --benchmark-tail-frames=N Frames at the end of the run the mean/median/p95 are\n"
<< " computed over (default: 200, clamped to the frame count)\n"
<< " --benchmark-finish=0/1 glFinish at every frame boundary so a frame time covers\n"
<< " GPU completion and not just CPU submission (default: 1)\n"
<< " --benchmark-result=PATH Timing JSON output (default: OUTPUT/benchmark.json)\n"
<< " --dump-fbo-attachments CALL:DIR[:FBO,FBO,...]\n"
<< " At CALL, write every colour attachment and the depth\n"
<< " attachment of every live framebuffer object into DIR as\n"
<< " fbo<N>-att<M>.png / fbo<N>-depth.png, plus a manifest.txt\n"
<< " of formats and per-channel statistics. Repeatable.\n";
}
for (int i = 1; i < argc; ++i) {
const std::string arg = argv[i];
std::string optionValue;
bool optionHasValue = false;
if (arg == "--trace") {
if (!ReadValue(argc, argv, i, request.tracePath)) return false;
} else if (arg == "--golden") {
if (!ReadValue(argc, argv, i, request.goldenPath)) return false;
} else if (arg == "--alternate-golden") {
std::string alternateGolden;
if (!ReadValue(argc, argv, i, alternateGolden)) return false;
if (!alternateGolden.empty()) {
request.alternateGoldenPaths.push_back(alternateGolden);
}
} else if (arg == "--diff") {
if (!ReadValue(argc, argv, i, request.diffPath)) return false;
} else if (arg == "--output") {
if (!ReadValue(argc, argv, i, request.outputDir)) return false;
} else if (arg == "--backend") {
if (!ReadValue(argc, argv, i, request.backend)) return false;
} else if (arg == "--mobilegl-library") {
if (!ReadValue(argc, argv, i, request.mobileGlLibrary)) return false;
} else if (arg == "--target-frame") {
if (!ReadInt(argc, argv, i, request.targetFrame)) return false;
} else if (arg == "--target-call") {
if (!ReadLongLong(argc, argv, i, request.targetCall)) return false;
} else if (arg == "--width") {
if (!ReadInt(argc, argv, i, request.width)) return false;
} else if (arg == "--height") {
if (!ReadInt(argc, argv, i, request.height)) return false;
} else if (arg == "--window-surface") {
request.usePbuffer = false;
} else if (arg == "--pbuffer-surface") {
request.usePbuffer = true;
} else if (arg == "--hold-ms") {
if (!ReadInt(argc, argv, i, request.holdMs)) return false;
} else if (arg == "--ssim-threshold") {
if (!ReadDouble(argc, argv, i, request.ssimThreshold)) return false;
} else if (arg == "--crop-x") {
if (!ReadInt(argc, argv, i, request.cropX)) return false;
} else if (arg == "--crop-y") {
if (!ReadInt(argc, argv, i, request.cropY)) return false;
} else if (arg == "--crop-width") {
if (!ReadInt(argc, argv, i, request.cropWidth)) return false;
} else if (arg == "--crop-height") {
if (!ReadInt(argc, argv, i, request.cropHeight)) return false;
} else if (arg == "--coherent-as-flush") {
request.coherentAsFlush = true;
} else if (arg == "--benchmark") {
request.benchmark = true;
} else if (MatchOption(arg, "--benchmark-tail-frames", optionValue, optionHasValue)) {
if (!optionHasValue && !ReadValue(argc, argv, i, optionValue)) return false;
request.benchmarkTailFrames = std::atoi(optionValue.c_str());
} else if (MatchOption(arg, "--benchmark-finish", optionValue, optionHasValue)) {
if (!optionHasValue && !ReadValue(argc, argv, i, optionValue)) return false;
request.benchmarkFinish = optionValue != "0";
} else if (MatchOption(arg, "--benchmark-result", optionValue, optionHasValue)) {
if (!optionHasValue && !ReadValue(argc, argv, i, optionValue)) return false;
request.benchmarkResultPath = optionValue;
} else if (arg == "--dump-fbo-attachments") {
std::string dumpPoint;
if (!ReadValue(argc, argv, i, dumpPoint)) return false;
if (dumpPoint.find(':') == std::string::npos) {
std::cerr << "--dump-fbo-attachments expects CALL:DIR[:FBO,FBO,...]\n";
return false;
}
request.fboAttachmentDumps.push_back(dumpPoint);
} else if (arg == "--help" || arg == "-h") {
return false;
} else {
std::cerr << "Unknown argument: " << arg << "\n";
bool ReadValue(int argc, char** argv, int& index, std::string& out) {
if (index + 1 >= argc) {
std::cerr << "Missing value for " << argv[index] << "\n";
return false;
}
out = argv[++index];
return true;
}
if (request.tracePath.empty()) {
std::cerr << "--trace is required\n";
bool ReadInt(int argc, char** argv, int& index, int& out) {
std::string value;
if (!ReadValue(argc, argv, index, value)) {
return false;
}
out = std::atoi(value.c_str());
return true;
}
bool ReadLongLong(int argc, char** argv, int& index, long long& out) {
std::string value;
if (!ReadValue(argc, argv, index, value)) {
return false;
}
out = std::strtoll(value.c_str(), nullptr, 10);
return true;
}
bool ReadDouble(int argc, char** argv, int& index, double& out) {
std::string value;
if (!ReadValue(argc, argv, index, value)) {
return false;
}
out = std::strtod(value.c_str(), nullptr);
return true;
}
// The benchmark options are documented as --name=VALUE. The space-separated spelling every
// other option here uses is accepted too, so --benchmark-tail-frames=300 and
// --benchmark-tail-frames 300 both work; hasInlineValue says which one was given.
bool MatchOption(const std::string& arg, const char* name, std::string& inlineValue, bool& hasInlineValue) {
if (arg == name) {
hasInlineValue = false;
return true;
}
const std::string prefix = std::string(name) + "=";
if (arg.compare(0, prefix.size(), prefix) == 0) {
inlineValue = arg.substr(prefix.size());
hasInlineValue = true;
return true;
}
return false;
}
if (request.outputDir.empty()) {
std::cerr << "--output is required\n";
return false;
bool ParseArgs(int argc, char** argv, mobilegl_trace::Request& request) {
request.backend = "DirectGLES";
// MOBILEGL_ITRP_FIX is a tri-state (Auto/ForceOn/ForceOff); the request only carries ForceOn.
if (const char* itrpFix = std::getenv("MOBILEGL_ITRP_FIX")) {
std::string value = itrpFix;
for (char& ch : value)
ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
request.iterationRPFix = value == "1" || value == "true" || value == "on" || value == "forceon";
}
for (int i = 1; i < argc; ++i) {
const std::string arg = argv[i];
std::string optionValue;
bool optionHasValue = false;
if (arg == "--trace") {
if (!ReadValue(argc, argv, i, request.tracePath)) return false;
} else if (arg == "--golden") {
if (!ReadValue(argc, argv, i, request.goldenPath)) return false;
} else if (arg == "--alternate-golden") {
std::string alternateGolden;
if (!ReadValue(argc, argv, i, alternateGolden)) return false;
if (!alternateGolden.empty()) {
request.alternateGoldenPaths.push_back(alternateGolden);
}
} else if (arg == "--diff") {
if (!ReadValue(argc, argv, i, request.diffPath)) return false;
} else if (arg == "--output") {
if (!ReadValue(argc, argv, i, request.outputDir)) return false;
} else if (arg == "--backend") {
if (!ReadValue(argc, argv, i, request.backend)) return false;
} else if (arg == "--mobilegl-library") {
if (!ReadValue(argc, argv, i, request.mobileGlLibrary)) return false;
} else if (arg == "--target-frame") {
if (!ReadInt(argc, argv, i, request.targetFrame)) return false;
} else if (arg == "--target-call") {
if (!ReadLongLong(argc, argv, i, request.targetCall)) return false;
} else if (arg == "--width") {
if (!ReadInt(argc, argv, i, request.width)) return false;
} else if (arg == "--height") {
if (!ReadInt(argc, argv, i, request.height)) return false;
} else if (arg == "--window-surface") {
request.usePbuffer = false;
} else if (arg == "--pbuffer-surface") {
request.usePbuffer = true;
} else if (arg == "--hold-ms") {
if (!ReadInt(argc, argv, i, request.holdMs)) return false;
} else if (arg == "--ssim-threshold") {
if (!ReadDouble(argc, argv, i, request.ssimThreshold)) return false;
} else if (arg == "--crop-x") {
if (!ReadInt(argc, argv, i, request.cropX)) return false;
} else if (arg == "--crop-y") {
if (!ReadInt(argc, argv, i, request.cropY)) return false;
} else if (arg == "--crop-width") {
if (!ReadInt(argc, argv, i, request.cropWidth)) return false;
} else if (arg == "--crop-height") {
if (!ReadInt(argc, argv, i, request.cropHeight)) return false;
} else if (arg == "--coherent-as-flush") {
request.coherentAsFlush = true;
} else if (arg == "--benchmark") {
request.benchmark = true;
} else if (MatchOption(arg, "--benchmark-tail-frames", optionValue, optionHasValue)) {
if (!optionHasValue && !ReadValue(argc, argv, i, optionValue)) return false;
request.benchmarkTailFrames = std::atoi(optionValue.c_str());
} else if (MatchOption(arg, "--benchmark-finish", optionValue, optionHasValue)) {
if (!optionHasValue && !ReadValue(argc, argv, i, optionValue)) return false;
request.benchmarkFinish = optionValue != "0";
} else if (MatchOption(arg, "--benchmark-result", optionValue, optionHasValue)) {
if (!optionHasValue && !ReadValue(argc, argv, i, optionValue)) return false;
request.benchmarkResultPath = optionValue;
} else if (arg == "--dump-fbo-attachments") {
std::string dumpPoint;
if (!ReadValue(argc, argv, i, dumpPoint)) return false;
if (dumpPoint.find(':') == std::string::npos) {
std::cerr << "--dump-fbo-attachments expects CALL:DIR[:FBO,FBO,...]\n";
return false;
}
request.fboAttachmentDumps.push_back(dumpPoint);
} else if (arg == "--help" || arg == "-h") {
return false;
} else {
std::cerr << "Unknown argument: " << arg << "\n";
return false;
}
}
if (request.tracePath.empty()) {
std::cerr << "--trace is required\n";
return false;
}
if (request.outputDir.empty()) {
std::cerr << "--output is required\n";
return false;
}
if (request.targetCall < 0 && !request.benchmark) {
std::cerr << "--target-call is required\n";
return false;
}
if (request.holdMs < 0) {
std::cerr << "--hold-ms must be non-negative\n";
return false;
}
if (request.benchmarkTailFrames <= 0) {
std::cerr << "--benchmark-tail-frames must be positive\n";
return false;
}
return true;
}
if (request.targetCall < 0 && !request.benchmark) {
std::cerr << "--target-call is required\n";
return false;
}
if (request.holdMs < 0) {
std::cerr << "--hold-ms must be non-negative\n";
return false;
}
if (request.benchmarkTailFrames <= 0) {
std::cerr << "--benchmark-tail-frames must be positive\n";
return false;
}
return true;
}
} // namespace
int main(int argc, char **argv) {
int main(int argc, char** argv) {
mobilegl_trace::Request request;
if (!ParseArgs(argc, argv, request)) {
PrintUsage(argv[0]);
+2 -2
View File
@@ -68,8 +68,8 @@ static LONG WINAPI CrashDump(EXCEPTION_POINTERS* ep) {
static volatile LONG entered = 0;
if (ep->ExceptionRecord->ExceptionCode != 0xC0000005) return EXCEPTION_CONTINUE_SEARCH;
if (InterlockedExchange(&entered, 1)) return EXCEPTION_CONTINUE_SEARCH;
HANDLE f = CreateFileA("glfw-crash-stack.txt", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, nullptr);
HANDLE f =
CreateFileA("glfw-crash-stack.txt", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (f == INVALID_HANDLE_VALUE) TerminateProcess(GetCurrentProcess(), 0x43);
RawPrint(f, "AV at ");
RawHex(f, (unsigned long long)ep->ExceptionRecord->ExceptionAddress);
+17 -15
View File
@@ -184,9 +184,9 @@ static LONG WINAPI CrashDump(EXCEPTION_POINTERS* ep) {
static volatile LONG entered = 0;
if (ep->ExceptionRecord->ExceptionCode != 0xC0000005) return EXCEPTION_CONTINUE_SEARCH;
if (InterlockedExchange(&entered, 1)) return EXCEPTION_CONTINUE_SEARCH;
HANDLE f = CreateFileA(
"C:\\Users\\yello\\AndroidStudioProjects\\FoldCraftLauncher\\MobileGL\\.claude\\worktrees\\wgl-host\\wgl-smoke\\crash-stack.txt",
GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
HANDLE f = CreateFileA("C:\\Users\\yello\\AndroidStudioProjects\\FoldCraftLauncher\\MobileGL\\."
"claude\\worktrees\\wgl-host\\wgl-smoke\\crash-stack.txt",
GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (f == INVALID_HANDLE_VALUE) {
TerminateProcess(GetCurrentProcess(), 0x43);
}
@@ -290,10 +290,8 @@ int main() {
eglGetError_ ? eglGetError_() : -1);
}
auto wglGetExtensionsStringARB =
(PFN_wglGetExtensionsStringARB)p_wglGetProcAddress("wglGetExtensionsStringARB");
auto wglCreateContextAttribsARB =
(PFN_wglCreateContextAttribsARB)p_wglGetProcAddress("wglCreateContextAttribsARB");
auto wglGetExtensionsStringARB = (PFN_wglGetExtensionsStringARB)p_wglGetProcAddress("wglGetExtensionsStringARB");
auto wglCreateContextAttribsARB = (PFN_wglCreateContextAttribsARB)p_wglGetProcAddress("wglCreateContextAttribsARB");
auto wglSwapIntervalEXT = (PFN_wglSwapIntervalEXT)p_wglGetProcAddress("wglSwapIntervalEXT");
auto wglGetPixelFormatAttribivARB =
(PFN_wglGetPixelFormatAttribivARB)p_wglGetProcAddress("wglGetPixelFormatAttribivARB");
@@ -326,7 +324,7 @@ int main() {
int chosenFormat = 0;
for (int i = 1; i <= nativeCount; ++i) {
const int attribs[] = {WGL_SUPPORT_OPENGL_ARB, WGL_DRAW_TO_WINDOW_ARB, WGL_PIXEL_TYPE_ARB,
WGL_DOUBLE_BUFFER_ARB, WGL_DEPTH_BITS_ARB, WGL_STENCIL_BITS_ARB};
WGL_DOUBLE_BUFFER_ARB, WGL_DEPTH_BITS_ARB, WGL_STENCIL_BITS_ARB};
int values[6] = {};
if (!wglGetPixelFormatAttribivARB(dc, i, 0, 6, attribs, values)) continue;
if (values[0] && values[1] && values[2] == WGL_TYPE_RGBA_ARB && values[3] && values[4] >= 24 &&
@@ -342,11 +340,16 @@ int main() {
if (!SetPixelFormat(dc, chosenFormat, &realPfd)) FAIL("SetPixelFormat (real) failed");
const int ctxAttribs[] = {
WGL_CONTEXT_OPENGL_NO_ERROR_ARB, 1,
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 2,
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
WGL_CONTEXT_OPENGL_NO_ERROR_ARB,
1,
WGL_CONTEXT_MAJOR_VERSION_ARB,
3,
WGL_CONTEXT_MINOR_VERSION_ARB,
2,
WGL_CONTEXT_FLAGS_ARB,
WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
WGL_CONTEXT_PROFILE_MASK_ARB,
WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0,
};
HGLRC ctx = wglCreateContextAttribsARB(dc, nullptr, ctxAttribs);
@@ -389,8 +392,7 @@ int main() {
GLint major = 0, minor = 0;
glGetIntegerv_(GL_MAJOR_VERSION, &major);
glGetIntegerv_(GL_MINOR_VERSION, &minor);
std::printf("real ctx: GL %d.%d | %s | %s\n", major, minor, glGetString_(GL_RENDERER),
glGetString_(GL_VERSION));
std::printf("real ctx: GL %d.%d | %s | %s\n", major, minor, glGetString_(GL_RENDERER), glGetString_(GL_VERSION));
if (major < 3 || (major == 3 && minor < 2)) FAIL("context version below 3.2");
const char* vsSrc = "#version 150 core\n"