diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index bca00d0a1..3bc5835d9 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -65,6 +65,12 @@ struct GLWindowingData #include "official/glxext.h" #endif #if RENDERDOC_SUPPORT_GLES + +// force include the elgplatform.h, as we want to use +// our own because the system one could be a bit older and +// propably not suitable for the given egl.h +#include "official/eglplatform.h" + #include "official/egl.h" #include "official/eglext.h" #endif diff --git a/renderdoc/driver/gl/gl_replay_egl.cpp b/renderdoc/driver/gl/gl_replay_egl.cpp index 99084f583..26539dbb7 100644 --- a/renderdoc/driver/gl/gl_replay_egl.cpp +++ b/renderdoc/driver/gl/gl_replay_egl.cpp @@ -27,26 +27,152 @@ #include "gl_driver.h" #include "gl_resources.h" +typedef EGLBoolean (*PFN_eglBindAPI)(EGLenum api); +typedef EGLDisplay (*PFN_eglGetDisplay)(EGLNativeDisplayType display_id); +typedef EGLContext (*PFN_eglCreateContext)(EGLDisplay dpy, EGLConfig config, + EGLContext share_context, const EGLint *attrib_list); +typedef EGLBoolean (*PFN_eglMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, + EGLContext ctx); +typedef EGLBoolean (*PFN_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface); +typedef EGLBoolean (*PFN_eglDestroyContext)(EGLDisplay dpy, EGLContext ctx); +typedef EGLBoolean (*PFN_eglQuerySurface)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, + EGLint *value); +typedef EGLBoolean (*PFN_eglDestroySurface)(EGLDisplay dpy, EGLSurface surface); +typedef EGLSurface (*PFN_eglCreatePbufferSurface)(EGLDisplay dpy, EGLConfig config, + const EGLint *attrib_list); +typedef EGLSurface (*PFN_eglCreateWindowSurface)(EGLDisplay dpy, EGLConfig config, + EGLNativeWindowType win, const EGLint *attrib_list); +typedef EGLBoolean (*PFN_eglChooseConfig)(EGLDisplay dpy, const EGLint *attrib_list, + EGLConfig *configs, EGLint config_size, EGLint *num_config); +typedef __eglMustCastToProperFunctionPointerType (*PFN_eglGetProcAddress)(const char *procname); +typedef EGLBoolean (*PFN_eglInitialize)(EGLDisplay dpy, EGLint *major, EGLint *minor); + +PFN_eglBindAPI eglBindAPIProc = NULL; +PFN_eglInitialize eglInitializeProc = NULL; +PFN_eglGetDisplay eglGetDisplayProc = NULL; +PFN_eglCreateContext eglCreateContextProc = NULL; +PFN_eglMakeCurrent eglMakeCurrentProc = NULL; +PFN_eglSwapBuffers eglSwapBuffersProc = NULL; +PFN_eglDestroyContext eglDestroyContextProc = NULL; +PFN_eglQuerySurface eglQuerySurfaceProc = NULL; +PFN_eglDestroySurface eglDestroySurfaceProc = NULL; +PFN_eglCreatePbufferSurface eglCreatePbufferSurfaceProc = NULL; +PFN_eglCreateWindowSurface eglCreateWindowSurfaceProc = NULL; +PFN_eglChooseConfig eglChooseConfigProc = NULL; +PFN_eglGetProcAddress eglGetProcAddressProc = NULL; + void GLReplay::MakeCurrentReplayContext(GLWindowingData *ctx) { - // TODO + static GLWindowingData *prev = NULL; + + if(eglMakeCurrentProc && ctx && ctx != prev) + { + prev = ctx; + eglMakeCurrentProc(ctx->egl_dpy, ctx->egl_wnd, ctx->egl_wnd, ctx->egl_ctx); + m_pDriver->ActivateContext(*ctx); + } } void GLReplay::SwapBuffers(GLWindowingData *ctx) { - // TODO + eglSwapBuffersProc(ctx->egl_dpy, ctx->egl_wnd); } void GLReplay::CloseReplayContext() { - // TODO + if(eglDestroyContextProc) + { + eglMakeCurrentProc(m_ReplayCtx.egl_dpy, 0L, 0L, NULL); + eglDestroyContextProc(m_ReplayCtx.egl_dpy, m_ReplayCtx.egl_ctx); + } } uint64_t GLReplay::MakeOutputWindow(WindowingSystem system, void *data, bool depth) { - // TODO + EGLNativeWindowType window = 0; + + if(system == eWindowingSystem_Xlib) + { + XlibWindowData *xlib = (XlibWindowData *)data; + window = (EGLNativeWindowType)xlib->window; + } + else if(system == eWindowingSystem_Unknown) + { + // allow undefined so that internally we can create a window-less context + Display *dpy = XOpenDisplay(NULL); + + if(dpy == NULL) + return 0; + } + else + { + RDCERR("Unexpected window system %u", system); + } + + EGLDisplay eglDisplay = eglGetDisplayProc(EGL_DEFAULT_DISPLAY); + RDCASSERT(eglDisplay); + + static const EGLint configAttribs[] = {EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES3_BIT, + EGL_SURFACE_TYPE, + EGL_PBUFFER_BIT | EGL_WINDOW_BIT, + EGL_NONE}; + EGLint numConfigs; + EGLConfig config; + + if(!eglChooseConfigProc(eglDisplay, configAttribs, &config, 1, &numConfigs)) + { + RDCERR("Couldn't find a suitable EGL config"); + return 0; + } + + static const EGLint ctxAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_CONTEXT_FLAGS_KHR, + EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL_NONE}; + + EGLContext ctx = eglCreateContextProc(eglDisplay, config, EGL_NO_CONTEXT, ctxAttribs); + + if(ctx == NULL) + { + RDCERR("Couldn't create GL ES context"); + return 0; + } + + EGLSurface surface = 0; + + if(window != 0) + { + surface = eglCreateWindowSurfaceProc(eglDisplay, config, window, NULL); + } + else + { + static const EGLint pbAttribs[] = {EGL_WIDTH, 32, EGL_HEIGHT, 32, EGL_NONE}; + surface = eglCreatePbufferSurfaceProc(eglDisplay, config, pbAttribs); + } + + RDCASSERT(surface != NULL); + + OutputWindow win; + win.egl_dpy = eglDisplay; + win.egl_ctx = ctx; + win.egl_wnd = surface; + + eglQuerySurfaceProc(eglDisplay, surface, EGL_WIDTH, &win.width); + eglQuerySurfaceProc(eglDisplay, surface, EGL_HEIGHT, &win.height); + + MakeCurrentReplayContext(&win); + InitOutputWindow(win); + CreateOutputWindowBackbuffer(win, depth); + uint64_t ret = m_OutputWindowID++; + m_OutputWindows[ret] = win; + return ret; } @@ -63,9 +189,10 @@ void GLReplay::DestroyOutputWindow(uint64_t id) WrappedOpenGL &gl = *m_pDriver; gl.glDeleteFramebuffers(1, &outw.BlitData.readFBO); - m_OutputWindows.erase(it); + eglMakeCurrentProc(outw.egl_dpy, 0L, 0L, NULL); + eglDestroyContextProc(outw.egl_dpy, outw.egl_ctx); - // TODO + m_OutputWindows.erase(it); } void GLReplay::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h) @@ -75,7 +202,8 @@ void GLReplay::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h) OutputWindow &outw = m_OutputWindows[id]; - // TODO + eglQuerySurfaceProc(outw.egl_dpy, outw.egl_wnd, EGL_WIDTH, &w); + eglQuerySurfaceProc(outw.egl_dpy, outw.egl_wnd, EGL_HEIGHT, &h); } bool GLReplay::IsOutputWindowVisible(uint64_t id) @@ -94,6 +222,159 @@ ReplayCreateStatus GL_CreateReplayDevice(const char *logfile, IReplayDriver **dr { RDCDEBUG("Creating an OpenGL ES replay device"); - // TODO - return eReplayCreate_APIInitFailed; + // Query the required EGL functions + if(eglCreateContextProc == NULL) + { + eglGetProcAddressProc = (PFN_eglGetProcAddress)dlsym(RTLD_NEXT, "eglGetProcAddress"); + eglChooseConfigProc = (PFN_eglChooseConfig)dlsym(RTLD_NEXT, "eglChooseConfig"); + eglInitializeProc = (PFN_eglInitialize)dlsym(RTLD_NEXT, "eglInitialize"); + eglBindAPIProc = (PFN_eglBindAPI)dlsym(RTLD_NEXT, "eglBindAPI"); + eglGetDisplayProc = (PFN_eglGetDisplay)dlsym(RTLD_NEXT, "eglGetDisplay"); + eglCreateContextProc = (PFN_eglCreateContext)dlsym(RTLD_NEXT, "eglCreateContext"); + eglMakeCurrentProc = (PFN_eglMakeCurrent)dlsym(RTLD_NEXT, "eglMakeCurrent"); + eglSwapBuffersProc = (PFN_eglSwapBuffers)dlsym(RTLD_NEXT, "eglSwapBuffers"); + eglDestroyContextProc = (PFN_eglDestroyContext)dlsym(RTLD_NEXT, "eglDestroyContext"); + eglDestroySurfaceProc = (PFN_eglDestroySurface)dlsym(RTLD_NEXT, "eglDestroySurface"); + eglQuerySurfaceProc = (PFN_eglQuerySurface)dlsym(RTLD_NEXT, "eglQuerySurface"); + eglCreatePbufferSurfaceProc = + (PFN_eglCreatePbufferSurface)dlsym(RTLD_NEXT, "eglCreatePbufferSurface"); + eglCreateWindowSurfaceProc = + (PFN_eglCreateWindowSurface)dlsym(RTLD_NEXT, "eglCreateWindowSurface"); + + if(eglGetProcAddressProc == NULL || eglBindAPIProc == NULL || eglGetDisplayProc == NULL || + eglCreateContextProc == NULL || eglMakeCurrentProc == NULL || eglSwapBuffersProc == NULL || + eglDestroyContextProc == NULL || eglDestroySurfaceProc == NULL || + eglQuerySurfaceProc == NULL || eglCreatePbufferSurfaceProc == NULL || + eglCreateWindowSurfaceProc == NULL || eglChooseConfigProc == NULL) + { + RDCERR( + "Couldn't find required function addresses, eglGetProcAddress eglCreateContext" + "eglSwapBuffers (etc.)"); + return eReplayCreate_APIInitFailed; + } + } + + GLInitParams initParams; + RDCDriver driverType = RDC_OpenGL; + string driverName = "OpenGL"; + uint64_t machineIdent = 0; + + if(logfile) + { + auto status = RenderDoc::Inst().FillInitParams(logfile, driverType, driverName, machineIdent, + (RDCInitParams *)&initParams); + if(status != eReplayCreate_Success) + return status; + } + + Display *dpy = XOpenDisplay(NULL); + + if(dpy == NULL) + { + RDCERR("Couldn't open default X display"); + return eReplayCreate_APIInitFailed; + } + + eglBindAPIProc(EGL_OPENGL_ES_API); + + EGLDisplay eglDisplay = eglGetDisplayProc(EGL_DEFAULT_DISPLAY); + if(!eglDisplay) + { + RDCERR("Couldn't open default EGL display"); + return eReplayCreate_APIInitFailed; + } + + int major, minor; + eglInitializeProc(eglDisplay, &major, &minor); + + static const EGLint configAttribs[] = {EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES3_BIT, + EGL_SURFACE_TYPE, + EGL_PBUFFER_BIT | EGL_WINDOW_BIT, + EGL_NONE}; + EGLint numConfigs; + EGLConfig config; + + if(!eglChooseConfigProc(eglDisplay, configAttribs, &config, 1, &numConfigs)) + { + RDCERR("Couldn't find a suitable EGL config"); + return eReplayCreate_APIInitFailed; + } + + static const EGLint ctxAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_CONTEXT_FLAGS_KHR, + EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR, EGL_NONE}; + + GLReplay::PreContextInitCounters(); + + EGLContext ctx = eglCreateContextProc(eglDisplay, config, EGL_NO_CONTEXT, ctxAttribs); + if(ctx == NULL) + { + XCloseDisplay(dpy); + GLReplay::PostContextShutdownCounters(); + RDCERR("Couldn't create GL ES 3.x context - RenderDoc requires OpenGL ES 3.x availability"); + return eReplayCreate_APIHardwareUnsupported; + } + + static const EGLint pbAttribs[] = {EGL_WIDTH, 32, EGL_HEIGHT, 32, EGL_NONE}; + EGLSurface pbuffer = eglCreatePbufferSurfaceProc(eglDisplay, config, pbAttribs); + + if(pbuffer == NULL) + { + RDCERR("Couldn't create a suitable PBuffer"); + eglDestroySurfaceProc(eglDisplay, pbuffer); + XCloseDisplay(dpy); + GLReplay::PostContextShutdownCounters(); + return eReplayCreate_APIInitFailed; + } + + EGLBoolean res = eglMakeCurrentProc(eglDisplay, pbuffer, pbuffer, ctx); + if(!res) + { + RDCERR("Couldn't active the created GL ES context"); + eglDestroySurfaceProc(eglDisplay, pbuffer); + eglDestroyContextProc(eglDisplay, ctx); + XCloseDisplay(dpy); + GLReplay::PostContextShutdownCounters(); + return eReplayCreate_APIInitFailed; + } + + // TODO: add extesion check just like in the GL case. + + const GLHookSet &real = GetRealGLFunctionsEGL(); + bool extensionsValidated = ValidateFunctionPointers(real); + if(!extensionsValidated) + { + eglDestroySurfaceProc(eglDisplay, pbuffer); + eglDestroyContextProc(eglDisplay, ctx); + XCloseDisplay(dpy); + GLReplay::PostContextShutdownCounters(); + return eReplayCreate_APIHardwareUnsupported; + } + + WrappedOpenGL *gl = new WrappedOpenGL(logfile, real); + gl->Initialise(initParams); + + if(gl->GetSerialiser()->HasError()) + { + delete gl; + return eReplayCreate_FileIOFailed; + } + + RDCLOG("Created OPEN GL ES replay device."); + GLReplay *replay = gl->GetReplay(); + replay->SetProxy(logfile == NULL); + GLWindowingData data; + data.egl_dpy = eglDisplay; + data.egl_ctx = ctx; + data.egl_wnd = pbuffer; + replay->SetReplayData(data); + + *driver = (IReplayDriver *)replay; + return eReplayCreate_Success; } diff --git a/renderdoc/driver/gl/official/eglplatform.h b/renderdoc/driver/gl/official/eglplatform.h new file mode 100644 index 000000000..7041b2fa5 --- /dev/null +++ b/renderdoc/driver/gl/official/eglplatform.h @@ -0,0 +1,133 @@ +#ifndef __eglplatform_h_ +#define __eglplatform_h_ + +/* +** Copyright (c) 2007-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Platform-specific types and definitions for egl.h + * $Revision: 30994 $ on $Date: 2015-04-30 13:36:48 -0700 (Thu, 30 Apr 2015) $ + * + * Adopters may modify khrplatform.h and this file to suit their platform. + * You are encouraged to submit all modifications to the Khronos group so that + * they can be included in future versions of this file. Please submit changes + * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla) + * by filing a bug against product "EGL" component "Registry". + */ + +#include + +/* Macros used in EGL function prototype declarations. + * + * EGL functions should be prototyped as: + * + * EGLAPI return-type EGLAPIENTRY eglFunction(arguments); + * typedef return-type (EXPAPIENTRYP PFNEGLFUNCTIONPROC) (arguments); + * + * KHRONOS_APICALL and KHRONOS_APIENTRY are defined in KHR/khrplatform.h + */ + +#ifndef EGLAPI +#define EGLAPI KHRONOS_APICALL +#endif + +#ifndef EGLAPIENTRY +#define EGLAPIENTRY KHRONOS_APIENTRY +#endif +#define EGLAPIENTRYP EGLAPIENTRY* + +/* The types NativeDisplayType, NativeWindowType, and NativePixmapType + * are aliases of window-system-dependent types, such as X Display * or + * Windows Device Context. They must be defined in platform-specific + * code below. The EGL-prefixed versions of Native*Type are the same + * types, renamed in EGL 1.3 so all types in the API start with "EGL". + * + * Khronos STRONGLY RECOMMENDS that you use the default definitions + * provided below, since these changes affect both binary and source + * portability of applications using EGL running on different EGL + * implementations. + */ + +#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include + +typedef HDC EGLNativeDisplayType; +typedef HBITMAP EGLNativePixmapType; +typedef HWND EGLNativeWindowType; + +#elif defined(__APPLE__) || defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */ + +typedef int EGLNativeDisplayType; +typedef void *EGLNativeWindowType; +typedef void *EGLNativePixmapType; + +#elif defined(__ANDROID__) || defined(ANDROID) + +#include + +struct egl_native_pixmap_t; + +typedef struct ANativeWindow* EGLNativeWindowType; +typedef struct egl_native_pixmap_t* EGLNativePixmapType; +typedef void* EGLNativeDisplayType; + +#elif defined(__unix__) + +/* X11 (tentative) */ +#include +#include + +typedef Display *EGLNativeDisplayType; +typedef Pixmap EGLNativePixmapType; +typedef Window EGLNativeWindowType; + +#else +#error "Platform not recognized" +#endif + +/* EGL 1.2 types, renamed for consistency in EGL 1.3 */ +typedef EGLNativeDisplayType NativeDisplayType; +typedef EGLNativePixmapType NativePixmapType; +typedef EGLNativeWindowType NativeWindowType; + + +/* Define EGLint. This must be a signed integral type large enough to contain + * all legal attribute names and values passed into and out of EGL, whether + * their type is boolean, bitmask, enumerant (symbolic constant), integer, + * handle, or other. While in general a 32-bit integer will suffice, if + * handles are 64 bit types, then EGLint should be defined as a signed 64-bit + * integer type. + */ +typedef khronos_int32_t EGLint; + + +/* C++ / C typecast macros for special EGL handle values */ +#if defined(__cplusplus) +#define EGL_CAST(type, value) (static_cast(value)) +#else +#define EGL_CAST(type, value) ((type) (value)) +#endif + +#endif /* __eglplatform_h */ diff --git a/renderdoccmd/CMakeLists.txt b/renderdoccmd/CMakeLists.txt index 9179decec..e8c8c4483 100644 --- a/renderdoccmd/CMakeLists.txt +++ b/renderdoccmd/CMakeLists.txt @@ -17,6 +17,10 @@ elseif(UNIX) list(APPEND libraries PRIVATE ${OPENGL_gl_LIBRARY}) endif() + if(ENABLE_GLES) + list(APPEND libraries PRIVATE -lEGL) + endif() + if(ENABLE_XLIB) list(APPEND libraries PRIVATE -lX11) endif() diff --git a/renderdoccmd/renderdoccmd_linux.cpp b/renderdoccmd/renderdoccmd_linux.cpp index 0cd10477e..6e298f4c3 100644 --- a/renderdoccmd/renderdoccmd_linux.cpp +++ b/renderdoccmd/renderdoccmd_linux.cpp @@ -708,6 +708,14 @@ extern "C" void glXWaitX(); #endif +#if defined(RENDERDOC_SUPPORT_GLES) + +// symbol defined in libEGL but not in librenderdoc. +// Forces link of libEGL. +extern "C" int eglWaitGL(void); + +#endif + void sig_handler(int signo) { if(usingKillSignal) @@ -726,6 +734,14 @@ int main(int argc, char *argv[]) if(never_run) glXWaitX(); +#endif + +#if defined(RENDERDOC_SUPPORT_GLES) + + volatile bool never_run = false; + if(never_run) + eglWaitGL(); + #endif signal(SIGINT, sig_handler); @@ -750,6 +766,11 @@ int main(int argc, char *argv[]) count++; #endif +#if defined(RENDERDOC_SUPPORT_GLES) + support += "GLES, "; + count++; +#endif + if(count == 0) { support += "None.";