| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "spy.h" |
| #include "connection_writer.h" |
| |
| #include <gapic/encoder.h> |
| #include <gapic/log.h> |
| #include <gapic/target.h> |
| #include <gapic/thread.h> |
| |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| #include "windows/wgl.h" |
| #endif // TARGET_OS |
| |
| namespace { |
| |
| const uint32_t EGL_WIDTH = 0x3057; |
| const uint32_t EGL_HEIGHT = 0x3056; |
| const uint32_t EGL_SWAP_BEHAVIOR = 0x3093; |
| const uint32_t EGL_BUFFER_PRESERVED = 0x3094; |
| |
| const uint32_t GLX_WIDTH = 0x801D; |
| const uint32_t GLX_HEIGHT = 0x801E; |
| |
| const uint32_t kCGLCPSurfaceBackingSize = 304; |
| |
| bool isLittleEndian() { |
| union { |
| uint32_t i; |
| char c[4]; |
| } u; |
| u.i = 0x01020304; |
| return u.c[0] == 4; |
| } |
| |
| } // anonymous namespace |
| |
| namespace gapii { |
| |
| // Use a "localabstract" pipe on Android to prevent depending on the traced application |
| // having the INTERNET permission set, required for opening and listening on a TCP socket. |
| Spy::Spy() { |
| #if TARGET_OS == GAPID_OS_ANDROID |
| auto writer = ConnectionWriter::listenPipe("gfxspy", true); |
| #else // TARGET_OS |
| auto writer = ConnectionWriter::listenSocket("127.0.0.1", "9286"); |
| #endif |
| auto encoder = std::shared_ptr<gapic::Encoder>(new gapic::Encoder(writer)); |
| GlesSpy::init(encoder); |
| GlesSpy::architecture(alignof(void*), sizeof(void*), sizeof(int), isLittleEndian()); |
| } |
| |
| void Spy::lock() { |
| SpyBase::lock(); |
| auto threadID = gapic::Thread::current().id(); |
| if (threadID != CurrentThread) { |
| GAPID_INFO("Changing threads: %" PRIu64 "-> %" PRIu64 "\n", CurrentThread, threadID); |
| GlesSpy::switchThread(threadID); |
| } |
| } |
| |
| EGLBoolean Spy::eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) { |
| EGLBoolean res = GlesSpy::eglInitialize(dpy, major, minor); |
| if (res != 0) { |
| mImports.Resolve(); |
| } |
| return res; |
| } |
| |
| EGLBoolean Spy::eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context) { |
| using namespace GLenum; |
| |
| EGLBoolean res = GlesSpy::eglMakeCurrent(display, draw, read, context); |
| if (res != 0 && draw != nullptr) { |
| int width = 0; |
| int height = 0; |
| int swapBehavior = 0; |
| mImports.eglQuerySurface(display, draw, EGL_WIDTH, &width); |
| mImports.eglQuerySurface(display, draw, EGL_HEIGHT, &height); |
| mImports.eglQuerySurface(display, draw, EGL_SWAP_BEHAVIOR, &swapBehavior); |
| |
| bool resetViewportScissor = true; |
| bool preserveBuffersOnSwap = swapBehavior == EGL_BUFFER_PRESERVED; |
| |
| // TODO: Probe formats |
| GlesSpy::backbufferInfo( |
| width, height, |
| GL_RGBA8, GL_DEPTH_COMPONENT16, GL_STENCIL_INDEX8, |
| resetViewportScissor, |
| preserveBuffersOnSwap); |
| } |
| |
| return res; |
| } |
| |
| BOOL Spy::wglMakeCurrent(HDC hdc, HGLRC hglrc) { |
| BOOL res = GlesSpy::wglMakeCurrent(hdc, hglrc); |
| if (res != 0 && hglrc != nullptr) { |
| mImports.Resolve(); |
| |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| wgl::FramebufferInfo info; |
| wgl::getFramebufferInfo(hdc, info); |
| GlesSpy::backbufferInfo( |
| info.width, info.height, |
| info.colorFormat, info.depthFormat, info.stencilFormat, |
| /* resetViewportScissor */ true, |
| /* preserveBuffersOnSwap */ false); |
| #endif // TARGET_OS |
| } |
| |
| return res; |
| } |
| |
| CGLError Spy::CGLSetCurrentContext(CGLContextObj ctx) { |
| using namespace GLenum; |
| |
| CGLError err = GlesSpy::CGLSetCurrentContext(ctx); |
| if (err == 0 && ctx != nullptr) { |
| CGSConnectionID cid; |
| CGSWindowID wid; |
| CGSSurfaceID sid; |
| double bounds[4]; |
| |
| if (mImports.CGLGetSurface(ctx, &cid, &wid, &sid) == 0) { |
| mImports.CGSGetSurfaceBounds(cid, wid, sid, bounds); |
| } |
| int width = bounds[2] - bounds[0]; // size.x - origin.x |
| int height = bounds[3] - bounds[1]; // size.y - origin.y |
| |
| // TODO: Probe formats |
| GlesSpy::backbufferInfo( |
| width, height, |
| GL_RGBA8, GL_DEPTH_COMPONENT16, GL_STENCIL_INDEX8, |
| /* resetViewportScissor */ true, |
| /* preserveBuffersOnSwap */ false); |
| } |
| return err; |
| } |
| |
| Bool Spy::glXMakeContextCurrent(void* display, GLXDrawable draw, GLXDrawable read, GLXContext ctx) { |
| using namespace GLenum; |
| |
| Bool res = GlesSpy::glXMakeContextCurrent(display, draw, read, ctx); |
| if (res != 0 && display != nullptr) { |
| int width = 0; |
| int height = 0; |
| mImports.glXQueryDrawable(display, draw, GLX_WIDTH, &width); |
| mImports.glXQueryDrawable(display, draw, GLX_HEIGHT, &height); |
| |
| // TODO: Probe formats |
| GlesSpy::backbufferInfo( |
| width, height, |
| GL_RGBA8, GL_DEPTH_COMPONENT16, GL_STENCIL_INDEX8, |
| /* resetViewportScissor */ true, |
| /* preserveBuffersOnSwap */ false); |
| } |
| |
| return res; |
| } |
| |
| Bool Spy::glXMakeCurrent(void* display, GLXDrawable drawable, GLXContext ctx) { |
| using namespace GLenum; |
| |
| Bool res = GlesSpy::glXMakeCurrent(display, drawable, ctx); |
| if (res != 0 && display != nullptr) { |
| int width = 0; |
| int height = 0; |
| mImports.glXQueryDrawable(display, drawable, GLX_WIDTH, &width); |
| mImports.glXQueryDrawable(display, drawable, GLX_HEIGHT, &height); |
| |
| // TODO: Probe formats |
| GlesSpy::backbufferInfo( |
| width, height, |
| GL_RGBA8, GL_DEPTH_COMPONENT16, GL_STENCIL_INDEX8, |
| /* resetViewportScissor */ true, |
| /* preserveBuffersOnSwap */ false); |
| } |
| |
| return res; |
| } |
| |
| } // namespace gapii |