Add experimental wayland support. Refs #853

* This is only lightly tested and may break heavily. It is disabled by default
 and must be explicitly enabled.
* In particular this is only known to work for Wayland use at capture time.
 Wayland on replay is still unsupported. Known issues include: EGL pbuffer
 surfaces are not implemented on Wayland, Wayland cannot get window dimensions,
 and there are hangs/failures with GL and vulkan presentation with Wayland.
This commit is contained in:
baldurk
2019-08-29 20:48:39 +01:00
parent 9b501d88a1
commit 6d40bbb783
48 changed files with 1051 additions and 185 deletions
+24
View File
@@ -166,6 +166,7 @@ option(ENABLE_PYRENDERDOC "Enable renderdoc python modules" ON)
option(ENABLE_XLIB "Enable xlib windowing support" ON)
option(ENABLE_XCB "Enable xcb windowing support" ON)
option(ENABLE_WAYLAND "Enable experimental wayland windowing support" OFF)
option(ENABLE_GGP "Enable GGP support" OFF)
option(ENABLE_ASAN "Enable address sanitizer" OFF)
@@ -182,6 +183,21 @@ if(ENABLE_TSAN)
message(STATUS "Enabling thread sanitizer - may cause issues if capturing, only recommended for use with replay only")
endif()
if(ENABLE_WAYLAND)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PKG_WAYLAND QUIET wayland-client)
if(PKG_WAYLAND_FOUND)
if(NOT ENABLE_GLES)
message(WARNING "On Wayland GLES support is required for EGL, disabling GL support")
set(ENABLE_GL OFF CACHE BOOL "" FORCE)
endif()
else()
message(WARNING "Wayland not found - disabling support")
set(ENABLE_WAYLAND OFF CACHE BOOL "" FORCE)
endif()
endif()
if(WIN32)
message(FATAL_ERROR "CMake is not needed on Windows, just open and build renderdoc.sln")
endif()
@@ -328,6 +344,10 @@ elseif(UNIX)
if(ENABLE_XCB)
add_definitions(-DRENDERDOC_WINDOWING_XCB)
endif()
if(ENABLE_WAYLAND)
add_definitions(-DRENDERDOC_WINDOWING_WAYLAND)
endif()
endif()
add_subdirectory(renderdoc)
@@ -379,5 +399,9 @@ if(UNIX AND NOT ANDROID AND NOT APPLE AND NOT ENABLE_GGP)
if(ENABLE_XCB)
message(STATUS " - XCB")
endif()
if(ENABLE_WAYLAND)
message(STATUS " - Wayland")
endif()
endif()
+3 -2
View File
@@ -373,10 +373,11 @@ The path follows the template set in :cpp:func:`SetCaptureFilePathTemplate` so i
* For D3D11 it must be the ``ID3D11Device`` device object.
* For D3D12 it must be the ``ID3D12Device`` device object.
* For OpenGL it must be the ``HGLRC`` or ``GLXContext`` context object.
* For OpenGL it must be the ``HGLRC``, ``GLXContext``, or ``EGLContext`` context object.
* For OpenGLES it must be the ``EGLContext`` context object.
* For Vulkan it must be the dispatch table pointer within the ``VkInstance``. This is a pointer-sized value at the location pointed to by the ``VkInstance``. NOTE - this is not the actual ``VkInstance`` pointer itself. You can use the RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE helper macro defined in the renderdoc header to obtain this pointer from any VkInstance.
``RENDERDOC_WindowHandle`` is a typedef to ``void *``. It is the platform specific ``HWND``, ``xcb_window_t``, or Xlib ``Window``.
``RENDERDOC_WindowHandle`` is a typedef to ``void *``. It is the platform specific Windows ``HWND``, Xcb ``xcb_window_t``, Xlib ``Window`` / ``Drawable``, Wayland ``wl_surface*``, or Android ``ANativeWindow*``.
.. cpp:function:: void StartFrameCapture(RENDERDOC_DevicePointer device, RENDERDOC_WindowHandle wndHandle)
+7
View File
@@ -168,6 +168,13 @@ if(CMAKE_COMPILER_IS_GNUCXX)
"QMAKE_CXXFLAGS+=-Wno-unknown-warning -Wno-implicit-fallthrough -Wno-cast-function-type -Wno-stringop-truncation\n")
endif()
if(ENABLE_WAYLAND)
message(WARNING "!!!! Using the Wayland Qt platform in qrenderdoc is NOT SUPPORTED !!!!")
file(APPEND
${CMAKE_BINARY_DIR}/qrenderdoc/qrenderdoc_cmake.pri
"DEFINES+=RENDERDOC_WINDOWING_WAYLAND\n")
endif()
# propagate build version info. Lots of escaping needed here to pass ""s into the define value
if(BUILD_VERSION_STABLE)
file(APPEND
+54 -12
View File
@@ -849,22 +849,48 @@ void CaptureContext::LoadCaptureThreaded(const QString &captureFile, const Repla
#if defined(RENDERDOC_PLATFORM_WIN32)
m_CurWinSystem = WindowingSystem::Win32;
#elif defined(RENDERDOC_PLATFORM_LINUX)
m_CurWinSystem = WindowingSystem::Xlib;
m_CurWinSystem = WindowingSystem::Unknown;
// prefer XCB, if supported
for(WindowingSystem sys : m_WinSystems)
if(QGuiApplication::platformName() == lit("wayland"))
{
if(sys == WindowingSystem::XCB)
// if we're using Wayland we must use wayland since we can't get any other surface type
for(WindowingSystem sys : m_WinSystems)
{
m_CurWinSystem = WindowingSystem::XCB;
break;
if(sys == WindowingSystem::Wayland)
{
m_CurWinSystem = WindowingSystem::Wayland;
m_WaylandDisplay = (wl_display *)AccessWaylandPlatformInterface("display", NULL);
break;
}
}
if(m_CurWinSystem == WindowingSystem::Unknown)
{
RDDialog::critical(NULL, tr("No wayland support"),
tr("Replay doesn't support Wayland surfaces - check you compiled this "
"build of RenderDoc with Wayland support enabled."));
}
}
if(m_CurWinSystem == WindowingSystem::XCB)
m_XCBConnection = QX11Info::connection();
else
m_X11Display = QX11Info::display();
{
m_CurWinSystem = WindowingSystem::Xlib;
// prefer XCB, if supported
for(WindowingSystem sys : m_WinSystems)
{
if(sys == WindowingSystem::XCB)
{
m_CurWinSystem = WindowingSystem::XCB;
break;
}
}
if(m_CurWinSystem == WindowingSystem::XCB)
m_XCBConnection = QX11Info::connection();
else
m_X11Display = QX11Info::display();
}
#elif defined(RENDERDOC_PLATFORM_APPLE)
m_CurWinSystem = WindowingSystem::MacOS;
#endif
@@ -1777,10 +1803,26 @@ WindowingData CaptureContext::CreateWindowingData(QWidget *window)
#elif defined(RENDERDOC_PLATFORM_LINUX)
if(m_CurWinSystem == WindowingSystem::XCB)
if(m_CurWinSystem == WindowingSystem::Wayland)
{
// we don't need this, we just need to force creation of a window handle
window->winId();
wl_surface *surface =
(wl_surface *)AccessWaylandPlatformInterface("surface", window->windowHandle());
return CreateWaylandWindowingData(m_WaylandDisplay, surface);
}
else if(m_CurWinSystem == WindowingSystem::XCB)
{
return CreateXCBWindowingData(m_XCBConnection, (xcb_window_t)window->winId());
else
}
else if(m_CurWinSystem == WindowingSystem::Xlib)
{
return CreateXlibWindowingData(m_X11Display, (Drawable)window->winId());
}
else
{
return CreateHeadlessWindowingData(1, 1);
}
#elif defined(RENDERDOC_PLATFORM_APPLE)
+1
View File
@@ -367,6 +367,7 @@ private:
WindowingSystem m_CurWinSystem = WindowingSystem::Unknown;
#if defined(RENDERDOC_PLATFORM_LINUX)
wl_display *m_WaylandDisplay = NULL;
xcb_connection_t *m_XCBConnection = NULL;
Display *m_X11Display = NULL;
#endif
+25
View File
@@ -2156,3 +2156,28 @@ QColor contrastingColor(const QColor &col, const QColor &defaultCol)
else
return QColor(Qt::black);
}
// we declare this partial class to get the accessors. THIS IS DANGEROUS as the ABI is unstable and
// this is a private class. The first few functions have been stable for a while so we hope that it
// will remain so. If a stable interface is added in future like QX11Info we should definitely use
// it instead.
//
// Unfortunately we need this for Wayland, so we only ever use it when we are absolutely forced to
// because we're running under the Wayland Qt platform.
class QOpenGLContext;
class Q_GUI_EXPORT QPlatformNativeInterface : public QObject
{
Q_OBJECT
public:
virtual void *nativeResourceForIntegration(const QByteArray &resource);
virtual void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context);
virtual void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen);
virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window);
};
void *AccessWaylandPlatformInterface(const QByteArray &resource, QWindow *window)
{
QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
return native->nativeResourceForWindow(resource, window);
}
+2
View File
@@ -609,3 +609,5 @@ bool IsDarkTheme();
float getLuminance(const QColor &col);
QColor contrastingColor(const QColor &col, const QColor &defaultCol);
void *AccessWaylandPlatformInterface(const QByteArray &resource, QWindow *window);
+1
View File
@@ -75,6 +75,7 @@
%typemap(in) Display* = HWND;
%typemap(in) xcb_connection_t* = HWND;
%typemap(in) wl_surface* = HWND;
// completely ignore rdcdatetime, we custom convert to/from a native python datetime
%ignore rdcdatetime;
+14
View File
@@ -358,6 +358,20 @@ int main(int argc, char *argv[])
GlobalEnvironment env;
#if defined(RENDERDOC_PLATFORM_LINUX)
env.xlibDisplay = QX11Info::display();
if(QGuiApplication::platformName() == lit("wayland"))
{
env.waylandDisplay = (wl_display *)AccessWaylandPlatformInterface("display", NULL);
QString warning =
tr("Running directly on Wayland is NOT SUPPORTED and is likely to crash, hang, or "
"fail to render.");
qInfo() << "------ !!!! WARNING !!!! ------";
qInfo() << warning;
qInfo() << "------ !!!! WARNING !!!! ------";
RDDialog::critical(NULL, tr("Wayland Qt platform not supported"), warning);
}
#endif
rdcarray<rdcstr> coreargs;
if(!crashReportPath.isEmpty())
+10
View File
@@ -70,6 +70,16 @@ elseif(UNIX)
list(APPEND RDOC_LIBRARIES
PRIVATE -l${XCB_LIBRARIES})
endif()
if(ENABLE_WAYLAND)
pkg_check_modules(PKG_WAYLAND QUIET wayland-client)
list(APPEND RDOC_INCLUDES
PRIVATE ${PKG_WAYLAND_INCLUDE_DIRS})
list(APPEND RDOC_LIBRARIES
PRIVATE -l${PKG_WAYLAND_LIBRARIES})
endif()
endif()
if(NOT "x${CMAKE_THREAD_LIBS_INIT}" STREQUAL "x")
+37
View File
@@ -330,6 +330,10 @@ DOCUMENT(R"(Specifies a windowing system to use for creating an output window.
The windowing data refers to an XCB window. See :func:`CreateXCBWindowingData`.
.. data:: Wayland
The windowing data refers to an Wayland window. See :func:`CreateWaylandWindowingData`.
.. data:: Android
The windowing data refers to an Android window. See :func:`CreateAndroidWindowingData`.
@@ -349,6 +353,7 @@ enum class WindowingSystem : uint32_t
Android,
MacOS,
GGP,
Wayland,
};
DECLARE_REFLECTION_ENUM(WindowingSystem);
@@ -368,6 +373,10 @@ typedef unsigned long Drawable;
struct xcb_connection_t;
typedef uint32_t xcb_window_t;
// wayland
struct wl_display;
struct wl_surface;
// android
struct ANativeWindow;
@@ -409,6 +418,12 @@ struct WindowingData
xcb_window_t window;
} xcb;
struct
{
wl_display *display;
wl_surface *window;
} wayland;
struct
{
ANativeWindow *window;
@@ -498,6 +513,24 @@ inline const WindowingData CreateXCBWindowingData(xcb_connection_t *connection,
return ret;
}
DOCUMENT(R"(Create a :class:`WindowingData` for an Wayland ``wl_surface`` handle.
:param wl_display display: The ``wl_display`` connection used for this window.
:param wl_surface window: The native ``wl_surface`` handle for this window.
:return: A :class:`WindowingData` corresponding to the given window.
:rtype: WindowingData
)");
inline const WindowingData CreateWaylandWindowingData(wl_display *display, wl_surface *window)
{
WindowingData ret = {};
ret.system = WindowingSystem::Wayland;
ret.wayland.display = display;
ret.wayland.window = window;
return ret;
}
DOCUMENT(R"(Create a :class:`WindowingData` for a GGP application.
:return: A :class:`WindowingData` corresponding to the given system.
@@ -571,6 +604,10 @@ struct GlobalEnvironment
DOCUMENT("The handle to the X display to use internally. If left ``NULL``, one will be opened.");
Display *xlibDisplay = NULL;
DOCUMENT(
"The handle to the X display to use internally. If left ``NULL``, wayland cannot be used.");
wl_display *waylandDisplay = NULL;
};
DOCUMENT("The result of executing or injecting into a program.")
+3
View File
@@ -60,11 +60,14 @@ rdcstr DoStringise(const WindowingSystem &el)
BEGIN_ENUM_STRINGISE(WindowingSystem)
{
STRINGISE_ENUM_CLASS(Unknown);
STRINGISE_ENUM_CLASS(Headless);
STRINGISE_ENUM_CLASS(Win32);
STRINGISE_ENUM_CLASS(Xlib);
STRINGISE_ENUM_CLASS(XCB);
STRINGISE_ENUM_CLASS(Android);
STRINGISE_ENUM_CLASS(MacOS);
STRINGISE_ENUM_CLASS(GGP);
STRINGISE_ENUM_CLASS(Wayland);
}
END_ENUM_STRINGISE();
}
+6
View File
@@ -129,6 +129,12 @@
#define RDOC_XCB OPTION_OFF
#endif
#if defined(RENDERDOC_WINDOWING_WAYLAND)
#define RDOC_WAYLAND OPTION_ON
#else
#define RDOC_WAYLAND OPTION_OFF
#endif
/////////////////////////////////////////////////
// Global constants
enum
+2 -2
View File
@@ -1323,7 +1323,7 @@ void WrappedID3D11Device::ReleaseSwapchainResources(IDXGISwapper *swapper, UINT
if(wnd)
{
Keyboard::RemoveInputWindow(wnd);
Keyboard::RemoveInputWindow(WindowingSystem::Win32, wnd);
RenderDoc::Inst().RemoveFrameCapturer((ID3D11Device *)this, wnd);
}
@@ -1460,7 +1460,7 @@ IUnknown *WrappedID3D11Device::WrapSwapchainBuffer(IDXGISwapper *swapper, DXGI_F
if(wnd)
{
Keyboard::AddInputWindow(wnd);
Keyboard::AddInputWindow(WindowingSystem::Win32, wnd);
RenderDoc::Inst().AddFrameCapturer((ID3D11Device *)this, wnd, this);
}
+2 -2
View File
@@ -870,7 +870,7 @@ void WrappedID3D12Device::ReleaseSwapchainResources(IDXGISwapper *swapper, UINT
if(wnd)
{
Keyboard::RemoveInputWindow(wnd);
Keyboard::RemoveInputWindow(WindowingSystem::Win32, wnd);
RenderDoc::Inst().RemoveFrameCapturer((ID3D12Device *)this, wnd);
}
@@ -1045,7 +1045,7 @@ IUnknown *WrappedID3D12Device::WrapSwapchainBuffer(IDXGISwapper *swapper, DXGI_F
if(wnd)
{
Keyboard::AddInputWindow(wnd);
Keyboard::AddInputWindow(WindowingSystem::Win32, wnd);
RenderDoc::Inst().AddFrameCapturer((ID3D12Device *)this, wnd, this);
}
+1 -1
View File
@@ -178,7 +178,7 @@ CGLError GL_EXPORT_NAME(CGLFlushDrawable)(CGLContextObj ctx)
CGL.CGLGetSurface(ctx, &conn, &window, &surface);
cglhook.driver.SwapBuffers((void *)(uintptr_t)window);
cglhook.driver.SwapBuffers(WindowingSystem::macOS, (void *)(uintptr_t)window);
}
CGLError ret;
+18 -11
View File
@@ -28,6 +28,8 @@
typedef EGLBoolean(EGLAPIENTRY *PFN_eglBindAPI)(EGLenum api);
typedef EGLDisplay(EGLAPIENTRY *PFN_eglGetDisplay)(EGLNativeDisplayType display_id);
typedef EGLDisplay(EGLAPIENTRY *PFN_eglGetPlatformDisplay)(EGLenum platform, void *native_display,
const EGLAttrib *attrib_list);
typedef EGLContext(EGLAPIENTRY *PFN_eglCreateContext)(EGLDisplay dpy, EGLConfig config,
EGLContext share_context,
const EGLint *attrib_list);
@@ -43,6 +45,9 @@ typedef EGLSurface(EGLAPIENTRY *PFN_eglCreatePbufferSurface)(EGLDisplay dpy, EGL
typedef EGLSurface(EGLAPIENTRY *PFN_eglCreateWindowSurface)(EGLDisplay dpy, EGLConfig config,
EGLNativeWindowType win,
const EGLint *attrib_list);
typedef EGLSurface(EGLAPIENTRY *PFN_eglCreatePlatformWindowSurface)(EGLDisplay dpy, EGLConfig config,
void *native_window,
const EGLAttrib *attrib_list);
typedef EGLBoolean(EGLAPIENTRY *PFN_eglChooseConfig)(EGLDisplay dpy, const EGLint *attrib_list,
EGLConfig *configs, EGLint config_size,
EGLint *num_config);
@@ -60,17 +65,19 @@ typedef PFNEGLPOSTSUBBUFFERNVPROC PFN_eglPostSubBufferNV;
typedef PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC PFN_eglSwapBuffersWithDamageEXT;
typedef PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC PFN_eglSwapBuffersWithDamageKHR;
#define EGL_HOOKED_SYMBOLS(FUNC) \
FUNC(BindAPI, false); \
FUNC(GetProcAddress, false); \
FUNC(GetDisplay, false); \
FUNC(CreateContext, false); \
FUNC(DestroyContext, false); \
FUNC(CreateWindowSurface, false); \
FUNC(MakeCurrent, false); \
FUNC(SwapBuffers, false); \
FUNC(PostSubBufferNV, true); \
FUNC(SwapBuffersWithDamageEXT, true); \
#define EGL_HOOKED_SYMBOLS(FUNC) \
FUNC(BindAPI, false); \
FUNC(GetProcAddress, false); \
FUNC(GetDisplay, false); \
FUNC(GetPlatformDisplay, false); \
FUNC(CreateContext, false); \
FUNC(DestroyContext, false); \
FUNC(CreateWindowSurface, false); \
FUNC(CreatePlatformWindowSurface, false); \
FUNC(MakeCurrent, false); \
FUNC(SwapBuffers, false); \
FUNC(PostSubBufferNV, true); \
FUNC(SwapBuffersWithDamageEXT, true); \
FUNC(SwapBuffersWithDamageKHR, true);
#define EGL_NONHOOKED_SYMBOLS(FUNC) \
+114 -17
View File
@@ -38,10 +38,23 @@
#if ENABLED(RDOC_LINUX)
namespace Keyboard
{
void CloneDisplay(Display *dpy);
void UseXlibDisplay(Display *dpy);
WindowingSystem UseUnknownDisplay(void *disp);
void UseWaylandDisplay(wl_display *disp);
}
#endif
struct SurfaceConfig
{
WindowingSystem system;
void *wnd;
};
struct DisplayConfig
{
WindowingSystem system;
};
class EGLHook : LibraryHook
{
public:
@@ -54,7 +67,8 @@ public:
WrappedOpenGL driver;
std::set<EGLContext> contexts;
std::map<EGLContext, EGLConfig> configs;
std::map<EGLSurface, EGLNativeWindowType> windows;
std::map<EGLSurface, SurfaceConfig> windows;
std::map<EGLDisplay, DisplayConfig> displays;
// indicates we're in a swap function, so don't process the swap any further if we recurse - could
// happen due to driver implementation of one function calling another
@@ -121,12 +135,15 @@ static void EnsureRealLibraryLoaded()
#if ENABLED(RDOC_LINUX)
if(eglhook.handle == DEFAULT_HANDLE)
{
RDCLOG("Loading libEGL at the last second");
if(!RenderDoc::Inst().IsReplayApp())
RDCLOG("Loading libEGL at the last second");
void *handle = Process::LoadModule("libEGL.so");
if(!handle)
handle = Process::LoadModule("libEGL.so.1");
eglhook.handle = handle;
}
#endif
}
@@ -144,12 +161,38 @@ HOOK_EXPORT EGLDisplay EGLAPIENTRY eglGetDisplay_renderdoc_hooked(EGLNativeDispl
EnsureRealLibraryLoaded();
#if ENABLED(RDOC_LINUX)
Keyboard::CloneDisplay(display);
Keyboard::UseUnknownDisplay((void *)display);
#endif
return EGL.GetDisplay(display);
}
HOOK_EXPORT EGLDisplay EGLAPIENTRY eglGetPlatformDisplay_renderdoc_hooked(EGLenum platform,
void *native_display,
const EGLAttrib *attrib_list)
{
if(RenderDoc::Inst().IsReplayApp())
{
if(!EGL.GetDisplay)
EGL.PopulateForReplay();
return EGL.GetPlatformDisplay(platform, native_display, attrib_list);
}
EnsureRealLibraryLoaded();
#if ENABLED(RDOC_LINUX)
if(platform == EGL_PLATFORM_X11_KHR)
Keyboard::UseXlibDisplay((Display *)native_display);
else if(platform == EGL_PLATFORM_WAYLAND_KHR)
Keyboard::UseWaylandDisplay((wl_display *)native_display);
else
RDCWARN("Unknown platform %x in eglGetPlatformDisplay", platform);
#endif
return EGL.GetPlatformDisplay(platform, native_display, attrib_list);
}
HOOK_EXPORT EGLBoolean EGLAPIENTRY eglBindAPI_renderdoc_hooked(EGLenum api)
{
if(RenderDoc::Inst().IsReplayApp())
@@ -336,7 +379,35 @@ HOOK_EXPORT EGLSurface EGLAPIENTRY eglCreateWindowSurface_renderdoc_hooked(EGLDi
{
SCOPED_LOCK(glLock);
eglhook.windows[ret] = win;
// spec says it's implementation dependent what happens, so we assume that we're using the same
// window system as the display
eglhook.windows[ret] = {eglhook.displays[dpy].system, (void *)win};
}
return ret;
}
HOOK_EXPORT EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface_renderdoc_hooked(
EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list)
{
if(RenderDoc::Inst().IsReplayApp())
{
if(!EGL.CreatePlatformWindowSurface)
EGL.PopulateForReplay();
return EGL.CreatePlatformWindowSurface(dpy, config, native_window, attrib_list);
}
EnsureRealLibraryLoaded();
EGLSurface ret = EGL.CreatePlatformWindowSurface(dpy, config, native_window, attrib_list);
if(ret)
{
SCOPED_LOCK(glLock);
// spec guarantees that we're using the same window system as the display
eglhook.windows[ret] = {eglhook.displays[dpy].system, native_window};
}
return ret;
@@ -376,17 +447,20 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglMakeCurrent_renderdoc_hooked(EGLDisplay di
GL.DriverForEmulation(&eglhook.driver);
}
SurfaceConfig cfg = eglhook.windows[draw];
GLWindowingData data;
data.egl_dpy = display;
data.egl_wnd = draw;
data.egl_ctx = ctx;
data.wnd = (decltype(data.wnd))eglhook.windows[draw];
data.wnd = (decltype(data.wnd))cfg.wnd;
if(!data.wnd)
{
// could be a pbuffer surface or other offscreen rendering. We want a valid wnd, so set it to
// a dummy value
data.wnd = (decltype(data.wnd))(void *)(uintptr_t(0xdeadbeef) + uintptr_t(draw));
cfg.system = WindowingSystem::Headless;
}
// we could query this out technically but it's easier to keep a map
@@ -426,7 +500,9 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglSwapBuffers_renderdoc_hooked(EGLDisplay dp
eglhook.RefreshWindowParameters(data);
eglhook.driver.SwapBuffers(surface);
SurfaceConfig cfg = eglhook.windows[surface];
eglhook.driver.SwapBuffers(cfg.system, cfg.wnd);
}
{
@@ -456,7 +532,11 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglPostSubBufferNV_renderdoc_hooked(EGLDispla
eglhook.driver.SetDriverType(eglhook.activeAPI);
if(!eglhook.driver.UsesVRFrameMarkers() && !eglhook.swapping)
eglhook.driver.SwapBuffers((void *)eglhook.windows[surface]);
{
SurfaceConfig cfg = eglhook.windows[surface];
eglhook.driver.SwapBuffers(cfg.system, cfg.wnd);
}
{
eglhook.swapping = true;
@@ -485,7 +565,11 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT_renderdoc_hooked(
eglhook.driver.SetDriverType(eglhook.activeAPI);
if(!eglhook.driver.UsesVRFrameMarkers() && !eglhook.swapping)
eglhook.driver.SwapBuffers((void *)eglhook.windows[surface]);
{
SurfaceConfig cfg = eglhook.windows[surface];
eglhook.driver.SwapBuffers(cfg.system, cfg.wnd);
}
{
eglhook.swapping = true;
@@ -514,7 +598,11 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR_renderdoc_hooked(
eglhook.driver.SetDriverType(eglhook.activeAPI);
if(!eglhook.driver.UsesVRFrameMarkers() && !eglhook.swapping)
eglhook.driver.SwapBuffers((void *)eglhook.windows[surface]);
{
SurfaceConfig cfg = eglhook.windows[surface];
eglhook.driver.SwapBuffers(cfg.system, cfg.wnd);
}
{
eglhook.swapping = true;
@@ -549,9 +637,9 @@ eglGetProcAddress_renderdoc_hooked(const char *func)
return realFunc;
// return our egl hooks
#define GPA_FUNCTION(name) \
if(!strcmp(func, "egl" STRINGIZE(name))) \
return (__eglMustCastToProperFunctionPointerType)&CONCAT(egl, CONCAT(name, _renderdoc_hooked)); \
#define GPA_FUNCTION(name, isext) \
if(!strcmp(func, "egl" STRINGIZE(name))) \
return (__eglMustCastToProperFunctionPointerType)&CONCAT(egl, CONCAT(name, _renderdoc_hooked));
EGL_HOOKED_SYMBOLS(GPA_FUNCTION)
#undef GPA_FUNCTION
@@ -578,6 +666,12 @@ HOOK_EXPORT EGLDisplay EGLAPIENTRY eglGetDisplay(EGLNativeDisplayType display)
return eglGetDisplay_renderdoc_hooked(display);
}
HOOK_EXPORT EGLDisplay EGLAPIENTRY eglGetPlatformDisplay(EGLenum platform, void *native_display,
const EGLAttrib *attrib_list)
{
return eglGetPlatformDisplay_renderdoc_hooked(platform, native_display, attrib_list);
}
HOOK_EXPORT EGLContext EGLAPIENTRY eglCreateContext(EGLDisplay display, EGLConfig config,
EGLContext shareContext, EGLint const *attribList)
{
@@ -596,6 +690,13 @@ HOOK_EXPORT EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy, EGLCon
return eglCreateWindowSurface_renderdoc_hooked(dpy, config, win, attrib_list);
}
HOOK_EXPORT EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config,
void *native_window,
const EGLAttrib *attrib_list)
{
return eglCreatePlatformWindowSurface_renderdoc_hooked(dpy, config, native_window, attrib_list);
}
HOOK_EXPORT EGLBoolean EGLAPIENTRY eglMakeCurrent(EGLDisplay display, EGLSurface draw,
EGLSurface read, EGLContext ctx)
{
@@ -757,10 +858,6 @@ EGL_PASSTHRU_4(EGLBoolean, eglGetSyncAttrib, EGLDisplay, dpy, EGLSync, sync, EGL
EGL_PASSTHRU_5(EGLImage, eglCreateImage, EGLDisplay, dpy, EGLContext, ctx, EGLenum, target,
EGLClientBuffer, buffer, const EGLAttrib *, attrib_list)
EGL_PASSTHRU_2(EGLBoolean, eglDestroyImage, EGLDisplay, dpy, EGLImage, image)
EGL_PASSTHRU_3(EGLDisplay, eglGetPlatformDisplay, EGLenum, platform, void *, native_display,
const EGLAttrib *, attrib_list)
EGL_PASSTHRU_4(EGLSurface, eglCreatePlatformWindowSurface, EGLDisplay, dpy, EGLConfig, config,
void *, native_window, const EGLAttrib *, attrib_list)
EGL_PASSTHRU_4(EGLSurface, eglCreatePlatformPixmapSurface, EGLDisplay, dpy, EGLConfig, config,
void *, native_pixmap, const EGLAttrib *, attrib_list)
EGL_PASSTHRU_3(EGLBoolean, eglWaitSync, EGLDisplay, dpy, EGLSync, sync, EGLint, flags)
+77 -23
View File
@@ -42,6 +42,8 @@ static void *GetEGLHandle()
class EGLPlatform : public GLPlatform
{
RDCDriver m_API = RDCDriver::OpenGLES;
bool MakeContextCurrent(GLWindowingData data)
{
if(EGL.MakeCurrent)
@@ -113,6 +115,7 @@ class EGLPlatform : public GLPlatform
bool IsOutputWindowVisible(GLWindowingData context) { return true; }
GLWindowingData MakeOutputWindow(WindowingData window, bool depth, GLWindowingData share_context)
{
EGLNativeDisplayType display = EGL_DEFAULT_DISPLAY;
EGLNativeWindowType win = 0;
switch(window.system)
@@ -122,7 +125,32 @@ class EGLPlatform : public GLPlatform
#elif ENABLED(RDOC_ANDROID)
case WindowingSystem::Android: win = window.android.window; break;
#elif ENABLED(RDOC_LINUX)
case WindowingSystem::Xlib: win = window.xlib.window; break;
case WindowingSystem::Xlib:
{
Display *xlibDisplay = RenderDoc::Inst().GetGlobalEnvironment().xlibDisplay;
display = (EGLNativeDisplayType)window.xlib.display;
win = (EGLNativeWindowType)window.xlib.window;
// ensure we're using the same display as the share context, and the same as our global
// display that we used at init time to create the share context's display
RDCASSERT((void *)display == (void *)xlibDisplay && display != NULL, (void *)display,
(void *)xlibDisplay);
break;
}
case WindowingSystem::Wayland:
{
wl_display *waylandDisplay = RenderDoc::Inst().GetGlobalEnvironment().waylandDisplay;
display = (EGLNativeDisplayType)window.wayland.display;
win = (EGLNativeWindowType)window.wayland.window;
// ensure we're using the same display as the share context, and the same as our global
// display
RDCASSERT((void *)display == (void *)waylandDisplay && display != NULL, (void *)display,
(void *)waylandDisplay);
break;
}
#endif
case WindowingSystem::Unknown:
case WindowingSystem::Headless:
@@ -131,9 +159,18 @@ class EGLPlatform : public GLPlatform
default: RDCERR("Unexpected window system %u", window.system); break;
}
EGLDisplay eglDisplay = EGL.GetDisplay(EGL_DEFAULT_DISPLAY);
EGLDisplay eglDisplay = NULL;
if(share_context.egl_dpy)
eglDisplay = share_context.egl_dpy;
else
eglDisplay = EGL.GetDisplay(display);
RDCASSERT(eglDisplay);
int major, minor;
EGL.Initialize(eglDisplay, &major, &minor);
return CreateWindowingData(eglDisplay, share_context.ctx, win, false);
}
@@ -150,24 +187,27 @@ class EGLPlatform : public GLPlatform
ret.egl_wnd = NULL;
EGLint surfaceType = (window == 0) ? EGL_PBUFFER_BIT : EGL_WINDOW_BIT;
const EGLint configAttribs[] = {EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES3_BIT,
EGL_CONFORMANT,
EGL_OPENGL_ES3_BIT,
EGL_SURFACE_TYPE,
surfaceType,
EGL_COLOR_BUFFER_TYPE,
EGL_RGB_BUFFER,
EGL_NONE};
const EGLint configAttribs[] = {
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_RENDERABLE_TYPE,
m_API == RDCDriver::OpenGLES ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_BIT,
EGL_CONFORMANT,
m_API == RDCDriver::OpenGLES ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_BIT,
EGL_SURFACE_TYPE,
surfaceType,
EGL_COLOR_BUFFER_TYPE,
EGL_RGB_BUFFER,
EGL_NONE,
};
EGLint numConfigs;
if(!EGL.ChooseConfig(eglDisplay, configAttribs, &ret.egl_cfg, 1, &numConfigs))
EGLint numConfigs = 0;
EGLBoolean success = EGL.ChooseConfig(eglDisplay, configAttribs, &ret.egl_cfg, 1, &numConfigs);
if(!success || numConfigs == 0)
{
RDCERR("Couldn't find a suitable EGL config");
return ret;
@@ -280,12 +320,26 @@ class EGLPlatform : public GLPlatform
bool PopulateForReplay() { return EGL.PopulateForReplay(); }
ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api, bool debug)
{
// we only support replaying GLES through EGL
RDCASSERT(api == RDCDriver::OpenGLES);
Display *xlibDisplay = RenderDoc::Inst().GetGlobalEnvironment().xlibDisplay;
wl_display *waylandDisplay = RenderDoc::Inst().GetGlobalEnvironment().waylandDisplay;
EGL.BindAPI(EGL_OPENGL_ES_API);
// we only support replaying GLES through EGL, except if Wayland is enabled
RDCASSERT(api == RDCDriver::OpenGLES || waylandDisplay);
EGLDisplay eglDisplay = EGL.GetDisplay(EGL_DEFAULT_DISPLAY);
m_API = api;
if(api == RDCDriver::OpenGLES)
EGL.BindAPI(EGL_OPENGL_ES_API);
else
EGL.BindAPI(EGL_OPENGL_API);
EGLNativeDisplayType display = EGL_DEFAULT_DISPLAY;
if(waylandDisplay)
display = (EGLNativeDisplayType)waylandDisplay;
else if(xlibDisplay)
display = (EGLNativeDisplayType)xlibDisplay;
EGLDisplay eglDisplay = EGL.GetDisplay(display);
if(!eglDisplay)
{
RDCERR("Couldn't open default EGL display");
+6 -1
View File
@@ -340,9 +340,14 @@ void GLReplay::InitDebugData()
RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.0f);
{
WindowingData window = {WindowingSystem::Unknown};
WindowingData window = {WindowingSystem::Headless};
uint64_t id = MakeOutputWindow(window, true);
m_DebugCtx = NULL;
if(id == 0)
return;
m_DebugID = id;
m_DebugCtx = &m_OutputWindows[id];
+18 -14
View File
@@ -1061,29 +1061,38 @@ void WrappedOpenGL::DeleteContext(void *contextHandle)
void *wndHandle = it->first;
it++;
ctxdata.UnassociateWindow(wndHandle);
ctxdata.UnassociateWindow(this, wndHandle);
}
m_ContextData.erase(contextHandle);
}
void WrappedOpenGL::ContextData::UnassociateWindow(void *wndHandle)
void WrappedOpenGL::ContextData::UnassociateWindow(WrappedOpenGL *driver, void *wndHandle)
{
auto it = windows.find(wndHandle);
if(it != windows.end())
{
if(it->second.first != WindowingSystem::Headless && IsCaptureMode(driver->GetState()))
Keyboard::RemoveInputWindow(it->second.first, wndHandle);
windows.erase(wndHandle);
RenderDoc::Inst().RemoveFrameCapturer(ctx, wndHandle);
}
}
void WrappedOpenGL::ContextData::AssociateWindow(WrappedOpenGL *driver, void *wndHandle)
void WrappedOpenGL::ContextData::AssociateWindow(WrappedOpenGL *driver, WindowingSystem winSystem,
void *wndHandle)
{
auto it = windows.find(wndHandle);
if(it == windows.end())
{
RenderDoc::Inst().AddFrameCapturer(ctx, wndHandle, driver);
windows[wndHandle] = Timing::GetUnixTimestamp();
if(winSystem != WindowingSystem::Headless && IsCaptureMode(driver->GetState()))
Keyboard::AddInputWindow(winSystem, wndHandle);
}
windows[wndHandle] = {winSystem, Timing::GetUnixTimestamp()};
}
void WrappedOpenGL::ContextData::CreateResourceRecord(WrappedOpenGL *driver, void *suppliedCtx)
@@ -1245,9 +1254,6 @@ void WrappedOpenGL::ActivateContext(GLWindowingData winData)
m_LastContexts.erase(m_LastContexts.begin());
}
// TODO: support multiple GL contexts more explicitly
Keyboard::AddInputWindow((void *)winData.wnd);
if(winData.ctx)
{
if(IsActiveCapturing(m_State))
@@ -1802,7 +1808,7 @@ void WrappedOpenGL::FreeTargetResource(ResourceId id)
}
}
void WrappedOpenGL::SwapBuffers(void *windowHandle)
void WrappedOpenGL::SwapBuffers(WindowingSystem winSystem, void *windowHandle)
{
if(IsBackgroundCapturing(m_State))
RenderDoc::Inst().Tick();
@@ -1840,9 +1846,9 @@ void WrappedOpenGL::SwapBuffers(void *windowHandle)
{
for(auto it = m_ContextData.begin(); it != m_ContextData.end(); ++it)
if(it->first != ctxdata.ctx)
it->second.UnassociateWindow(windowHandle);
it->second.UnassociateWindow(this, windowHandle);
ctxdata.AssociateWindow(this, windowHandle);
ctxdata.AssociateWindow(this, winSystem, windowHandle);
}
// we used to do this here so it was as late as possible to avoid creating objects on contexts
@@ -1863,14 +1869,12 @@ void WrappedOpenGL::SwapBuffers(void *windowHandle)
{
for(auto wit = cit->second.windows.begin(); wit != cit->second.windows.end();)
{
if(wit->second < ref)
if(wit->second.second < ref)
{
auto remove = wit;
++wit;
RenderDoc::Inst().RemoveFrameCapturer(cit->first, remove->first);
cit->second.windows.erase(remove);
cit->second.UnassociateWindow(this, remove->first);
}
else
{
+8 -8
View File
@@ -359,17 +359,17 @@ private:
GLInitParams initParams;
// map from window handle void* to uint64_t unix timestamp with
// the last time a window was seen/associated with this context.
// Decays after a few seconds since there's no good explicit
// 'remove' type call for GL, only wglCreateContext/wglMakeCurrent
std::map<void *, uint64_t> windows;
// map from window handle void* to the windowing system used and the uint64_t unix timestamp of
// the last time a window was seen/associated with this context. Decays after a few seconds
// since there's no good explicit 'remove' type call for GL, only
// wglCreateContext/wglMakeCurrent
std::map<void *, rdcpair<WindowingSystem, uint64_t>> windows;
// a window is only associated with one context at once, so any
// time we associate a window, it broadcasts to all other
// contexts to let them know to remove it
void UnassociateWindow(void *wndHandle);
void AssociateWindow(WrappedOpenGL *driver, void *wndHandle);
void UnassociateWindow(WrappedOpenGL *driver, void *wndHandle);
void AssociateWindow(WrappedOpenGL *driver, WindowingSystem winSystem, void *wndHandle);
void CreateDebugData();
@@ -595,7 +595,7 @@ public:
}
void ActivateContext(GLWindowingData winData);
bool ForceSharedObjects(void *oldContext, void *newContext);
void SwapBuffers(void *windowHandle);
void SwapBuffers(WindowingSystem winSystem, void *windowHandle);
void HandleVRFrameMarkers(const GLchar *buf, GLsizei length);
bool UsesVRFrameMarkers() { return m_UsesVRMarkers; }
void FirstFrame(void *ctx, void *wndHandle);
+60 -28
View File
@@ -140,12 +140,24 @@ std::vector<WindowingSystem> GLReplay::GetSupportedWindowSystems()
std::vector<WindowingSystem> ret;
#if ENABLED(RDOC_LINUX)
// only Xlib supported for GLX. We can't report XCB here since we need
// the Display, and that can't be obtained from XCB. The application is
// free to use XCB internally but it would have to create a hybrid and
// initialise XCB out of Xlib, to be able to provide the display and
// drawable to us.
ret.push_back(WindowingSystem::Xlib);
#if ENABLED(RDOC_WAYLAND)
// if wayland is supported and a display is configured, we *must* get wayland surfaces to render
// on
if(RenderDoc::Inst().GetGlobalEnvironment().waylandDisplay)
{
ret.push_back(WindowingSystem::Wayland);
}
else
#endif
{
// only Xlib supported for GLX. We can't report XCB here since we need the Display, and that
// can't be obtained from XCB. The application is free to use XCB internally but it would have
// to create a hybrid and initialise XCB out of Xlib, to be able to provide the display and
// drawable to us.
ret.push_back(WindowingSystem::Xlib);
}
#elif ENABLED(RDOC_ANDROID)
ret.push_back(WindowingSystem::Android);
#elif ENABLED(RDOC_APPLE)
@@ -250,6 +262,9 @@ void GLReplay::SetReplayData(GLWindowingData data)
InitDebugData();
if(!HasDebugContext())
return;
AMDCounters *countersAMD = NULL;
IntelGlCounters *countersIntel = NULL;
@@ -3465,6 +3480,12 @@ ReplayStatus CreateReplayDevice(RDCDriver rdcdriver, RDCFile *rdc, const ReplayO
replay->SetProxy(rdc == NULL);
replay->SetReplayData(data);
if(!replay->HasDebugContext())
{
platform.DeleteReplayContext(data);
return ReplayStatus::APIHardwareUnsupported;
}
gldriver->Initialise(initParams, ver, opts);
*driver = (IReplayDriver *)replay;
@@ -3510,28 +3531,6 @@ std::vector<GLVersion> GetReplayVersions(RDCDriver api)
}
}
#if defined(RENDERDOC_SUPPORT_GL)
ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver)
{
RDCDEBUG("Creating an OpenGL replay device");
bool load_ok = GetGLPlatform().PopulateForReplay();
if(!load_ok)
{
RDCERR("Couldn't find required platform GL function addresses");
return ReplayStatus::APIInitFailed;
}
return CreateReplayDevice(rdc ? rdc->GetDriver() : RDCDriver::OpenGL, rdc, opts, GetGLPlatform(),
driver);
}
static DriverRegistration GLDriverRegistration(RDCDriver::OpenGL, &GL_CreateReplayDevice);
#endif
#if defined(RENDERDOC_SUPPORT_GLES)
ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver)
@@ -3587,3 +3586,36 @@ ReplayStatus GLES_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IR
static DriverRegistration GLESDriverRegistration(RDCDriver::OpenGLES, &GLES_CreateReplayDevice);
#endif
#if defined(RENDERDOC_SUPPORT_GL)
ReplayStatus GL_CreateReplayDevice(RDCFile *rdc, const ReplayOptions &opts, IReplayDriver **driver)
{
if(RenderDoc::Inst().GetGlobalEnvironment().waylandDisplay)
{
#if defined(RENDERDOC_SUPPORT_GLES)
RDCLOG("Forcing EGL device creation for wayland");
return GLES_CreateReplayDevice(rdc, opts, driver);
#else
RDCERR("GLES support must be enabled at build time when using Wayland");
return ReplayStatus::InternalError;
#endif
}
RDCDEBUG("Creating an OpenGL replay device");
bool load_ok = GetGLPlatform().PopulateForReplay();
if(!load_ok)
{
RDCERR("Couldn't find required platform GL function addresses");
return ReplayStatus::APIInitFailed;
}
return CreateReplayDevice(rdc ? rdc->GetDriver() : RDCDriver::OpenGL, rdc, opts, GetGLPlatform(),
driver);
}
static DriverRegistration GLDriverRegistration(RDCDriver::OpenGL, &GL_CreateReplayDevice);
#endif
+1
View File
@@ -234,6 +234,7 @@ public:
void SetReplayData(GLWindowingData data);
bool IsReplayContext(void *ctx) { return m_ReplayCtx.ctx == NULL || ctx == m_ReplayCtx.ctx; }
bool HasDebugContext() { return m_DebugCtx != NULL; }
private:
void OpenGLFillCBufferVariables(GLuint prog, bool bufferBacked, std::string prefix,
const rdcarray<ShaderConstant> &variables,
+8 -8
View File
@@ -29,7 +29,7 @@
namespace Keyboard
{
void CloneDisplay(Display *dpy);
void UseXlibDisplay(Display *dpy);
}
class GLXHook : LibraryHook
@@ -65,18 +65,18 @@ public:
// callback and we'll get a specific library handle.
static void EnsureRealLibraryLoaded()
{
if(RenderDoc::Inst().IsReplayApp())
return;
if(glxhook.handle == RTLD_NEXT)
{
RDCLOG("Loading libGL at the last second");
if(!RenderDoc::Inst().IsReplayApp())
RDCLOG("Loading libGL at the last second");
void *handle = Process::LoadModule("libGL.so.1");
if(!handle)
handle = Process::LoadModule("libGL.so");
if(!handle)
handle = Process::LoadModule("libGLX.so.0");
glxhook.handle = handle;
}
}
@@ -106,7 +106,7 @@ HOOK_EXPORT GLXContext glXCreateContext_renderdoc_hooked(Display *dpy, XVisualIn
int value = 0;
Keyboard::CloneDisplay(dpy);
Keyboard::UseXlibDisplay(dpy);
GLX.glXGetConfig(dpy, vis, GLX_BUFFER_SIZE, &value);
init.colorBits = value;
@@ -251,7 +251,7 @@ HOOK_EXPORT GLXContext glXCreateContextAttribsARB_renderdoc_hooked(Display *dpy,
int value = 0;
Keyboard::CloneDisplay(dpy);
Keyboard::UseXlibDisplay(dpy);
GLX.glXGetConfig(dpy, vis, GLX_BUFFER_SIZE, &value);
init.colorBits = value;
@@ -444,7 +444,7 @@ HOOK_EXPORT void glXSwapBuffers_renderdoc_hooked(Display *dpy, GLXDrawable drawa
glxhook.UpdateWindowSize(data, dpy, drawable);
}
glxhook.driver.SwapBuffers((void *)drawable);
glxhook.driver.SwapBuffers(WindowingSystem::Xlib, (void *)drawable);
GLX.glXSwapBuffers(dpy, drawable);
}
+1 -1
View File
@@ -181,7 +181,7 @@ void WGLHook::ProcessSwapBuffers(HDC dc)
{
SCOPED_LOCK(glLock);
driver.SwapBuffers(w);
driver.SwapBuffers(WindowingSystem::Win32, w);
}
SetLastError(0);
@@ -293,7 +293,7 @@ void WrappedOpenGL::HandleVRFrameMarkers(const GLchar *buf, GLsizei length)
{
if(strstr(buf, "vr-marker,frame_end,type,application") != NULL)
{
SwapBuffers((void *)m_ActiveContexts[Threading::GetCurrentID()].wnd);
SwapBuffers(WindowingSystem::Headless, (void *)m_ActiveContexts[Threading::GetCurrentID()].wnd);
m_UsesVRMarkers = true;
if(IsActiveCapturing(m_State))
@@ -401,7 +401,7 @@ void WrappedOpenGL::glInsertEventMarkerEXT(GLsizei length, const GLchar *marker)
void WrappedOpenGL::glFrameTerminatorGREMEDY()
{
SwapBuffers((void *)m_ActiveContexts[Threading::GetCurrentID()].wnd);
SwapBuffers(WindowingSystem::Headless, (void *)m_ActiveContexts[Threading::GetCurrentID()].wnd);
}
void WrappedOpenGL::glStringMarkerGREMEDY(GLsizei len, const void *string)
+4
View File
@@ -111,6 +111,10 @@ elseif(UNIX)
add_definitions(-DVK_USE_PLATFORM_XCB_KHR)
endif()
if(ENABLE_WAYLAND)
add_definitions(-DVK_USE_PLATFORM_WAYLAND_KHR)
endif()
set(VULKAN_LAYER_MODULE_PATH "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/${LIB_SUBFOLDER_TRAIL_SLASH}librenderdoc.so")
set(json_in ${CMAKE_CURRENT_SOURCE_DIR}/renderdoc.json)
+2 -3
View File
@@ -43,9 +43,8 @@ VkResult WrappedVulkan::vkCreateAndroidSurfaceKHR(VkInstance instance,
WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
// since there's no point in allocating a full resource record and storing the window
// handle under there somewhere, we just cast. We won't use the resource record for anything
wrapped->record = (VkResourceRecord *)(uintptr_t)pCreateInfo->window;
wrapped->record =
PackWindowHandleInRecord(WindowingSystem::Android, (void *)(uintptr_t)pCreateInfo->window);
}
return ret;
+4 -6
View File
@@ -51,9 +51,8 @@ VkResult WrappedVulkan::vkCreateMacOSSurfaceMVK(VkInstance instance,
WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
// since there's no point in allocating a full resource record and storing the window
// handle under there somewhere, we just cast. We won't use the resource record for anything
wrapped->record = (VkResourceRecord *)(uintptr_t)pCreateInfo->pView;
wrapped->record =
PackWindowHandleInRecord(WindowingSystem::MacOS, (void *)(uintptr_t)pCreateInfo->pView);
}
return ret;
@@ -80,9 +79,8 @@ VkResult WrappedVulkan::vkCreateMetalSurfaceEXT(VkInstance instance,
WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
// since there's no point in allocating a full resource record and storing the window
// handle under there somewhere, we just cast. We won't use the resource record for anything
wrapped->record = (VkResourceRecord *)(uintptr_t)pCreateInfo->pLayer;
wrapped->record =
PackWindowHandleInRecord(WindowingSystem::MacOS, (void *)(uintptr_t)pCreateInfo->pLayer);
}
return ret;
+14
View File
@@ -101,6 +101,20 @@ int SampleCount(VkSampleCountFlagBits countFlag);
int SampleIndex(VkSampleCountFlagBits countFlag);
int StageIndex(VkShaderStageFlagBits stageFlag);
struct PackedWindowHandle
{
PackedWindowHandle(WindowingSystem s, void *h) : system(s), handle(h) {}
WindowingSystem system;
void *handle;
};
struct VkResourceRecord;
inline VkResourceRecord *PackWindowHandleInRecord(WindowingSystem system, void *handle)
{
return (VkResourceRecord *)new PackedWindowHandle(system, handle);
}
class WrappedVulkan;
struct VkPackedVersion
+5
View File
@@ -1010,6 +1010,11 @@ static const VkExtensionProperties supportedExtensions[] = {
{
VK_KHR_VULKAN_MEMORY_MODEL_EXTENSION_NAME, VK_KHR_VULKAN_MEMORY_MODEL_SPEC_VERSION,
},
#ifdef VK_KHR_wayland_surface
{
VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, VK_KHR_WAYLAND_SURFACE_SPEC_VERSION,
},
#endif
#ifdef VK_KHR_win32_keyed_mutex
{
VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME, VK_KHR_WIN32_KEYED_MUTEX_SPEC_VERSION,
+11
View File
@@ -1697,6 +1697,17 @@ public:
VkSurfaceKHR *pSurface);
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
// VK_KHR_wayland_surface
VkResult vkCreateWaylandSurfaceKHR(VkInstance instance,
const VkWaylandSurfaceCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface);
VkBool32 vkGetPhysicalDeviceWaylandPresentationSupportKHR(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
struct wl_display *display);
#endif
// VK_KHR_display and VK_KHR_display_swapchain. These have no library or include dependencies so
// wecan just compile them in on all platforms to reduce platform-specific code. They are mostly
// only actually used though on *nix.
+2 -1
View File
@@ -43,7 +43,8 @@ VkResult WrappedVulkan::vkCreateStreamDescriptorSurfaceGGP(
WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
wrapped->record = (VkResourceRecord *)(uintptr_t)pCreateInfo->streamDescriptor;
wrapped->record = PackWindowHandleInRecord(WindowingSystem::GGP,
(void *)(uintptr_t)pCreateInfo->streamDescriptor);
}
return ret;
}
+30 -5
View File
@@ -174,6 +174,26 @@
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
#define HookInitInstance_PlatformSpecific_Wayland() \
HookInitExtension(VK_KHR_wayland_surface, CreateWaylandSurfaceKHR); \
HookInitExtension(VK_KHR_wayland_surface, GetPhysicalDeviceWaylandPresentationSupportKHR);
#define HookDefine_PlatformSpecific_Wayland() \
HookDefine4(VkResult, vkCreateWaylandSurfaceKHR, VkInstance, instance, \
const VkWaylandSurfaceCreateInfoKHR *, pCreateInfo, const VkAllocationCallbacks *, \
pAllocator, VkSurfaceKHR *, pSurface); \
HookDefine3(VkBool32, vkGetPhysicalDeviceWaylandPresentationSupportKHR, VkPhysicalDevice, \
physicalDevice, uint32_t, queueFamilyIndex, struct wl_display *, display);
#else
#define HookInitInstance_PlatformSpecific_Wayland()
#define HookDefine_PlatformSpecific_Wayland()
#endif
#if defined(VK_USE_PLATFORM_XLIB_KHR)
#define HookInitInstance_PlatformSpecific_Xlib() \
@@ -200,11 +220,14 @@
#endif
#define HookInitInstance_PlatformSpecific() \
HookInitInstance_PlatformSpecific_Xcb() HookInitInstance_PlatformSpecific_Xlib()
#define HookInitInstance_PlatformSpecific() \
HookInitInstance_PlatformSpecific_Xcb() HookInitInstance_PlatformSpecific_Xlib() \
HookInitInstance_PlatformSpecific_Wayland()
#define HookInitDevice_PlatformSpecific()
#define HookDefine_PlatformSpecific() \
HookDefine_PlatformSpecific_Xcb() HookDefine_PlatformSpecific_Xlib()
#define HookDefine_PlatformSpecific() \
HookDefine_PlatformSpecific_Xcb() HookDefine_PlatformSpecific_Xlib() \
HookDefine_PlatformSpecific_Wayland()
#endif
@@ -375,6 +398,7 @@
DeclExt(KHR_get_display_properties2); \
DeclExt(EXT_headless_surface); \
DeclExt(EXT_metal_surface); \
DeclExt(KHR_wayland_surface); \
/* device extensions */ \
DeclExt(EXT_debug_marker); \
DeclExt(GGP_frame_token); \
@@ -457,7 +481,8 @@
CheckExt(EXT_calibrated_timestamps, VKXX); \
CheckExt(EXT_full_screen_exclusive, VKXX); \
CheckExt(EXT_headless_surface, VKXX); \
CheckExt(EXT_metal_surface, VKXX);
CheckExt(EXT_metal_surface, VKXX); \
CheckExt(KHR_wayland_surface, VKXX);
#define CheckDeviceExts() \
CheckExt(EXT_debug_marker, VKXX); \
+95 -10
View File
@@ -41,7 +41,7 @@ VkBool32 WrappedVulkan::vkGetPhysicalDeviceXcbPresentationSupportKHR(VkPhysicalD
namespace Keyboard
{
void UseConnection(xcb_connection_t *conn);
void UseXcbConnection(xcb_connection_t *conn);
}
VkResult WrappedVulkan::vkCreateXcbSurfaceKHR(VkInstance instance,
@@ -61,11 +61,57 @@ VkResult WrappedVulkan::vkCreateXcbSurfaceKHR(VkInstance instance,
WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
// since there's no point in allocating a full resource record and storing the window
// handle under there somewhere, we just cast. We won't use the resource record for anything
wrapped->record = (VkResourceRecord *)(uintptr_t)pCreateInfo->window;
wrapped->record =
PackWindowHandleInRecord(WindowingSystem::XCB, (void *)(uintptr_t)pCreateInfo->window);
Keyboard::UseConnection(pCreateInfo->connection);
Keyboard::UseXcbConnection(pCreateInfo->connection);
Keyboard::AddInputWindow(WindowingSystem::Xlib, (void *)(uintptr_t)pCreateInfo->window);
}
return ret;
}
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
namespace Keyboard
{
void UseWaylandDisplay(wl_display *disp);
}
VkBool32 WrappedVulkan::vkGetPhysicalDeviceWaylandPresentationSupportKHR(
VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, struct wl_display *display)
{
return ObjDisp(physicalDevice)
->GetPhysicalDeviceWaylandPresentationSupportKHR(Unwrap(physicalDevice), queueFamilyIndex,
display);
}
VkResult WrappedVulkan::vkCreateWaylandSurfaceKHR(VkInstance instance,
const VkWaylandSurfaceCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkSurfaceKHR *pSurface)
{
// should not come in here at all on replay
RDCASSERT(IsCaptureMode(m_State));
VkResult ret = ObjDisp(instance)->CreateWaylandSurfaceKHR(Unwrap(instance), pCreateInfo,
pAllocator, pSurface);
if(ret == VK_SUCCESS)
{
GetResourceManager()->WrapResource(Unwrap(instance), *pSurface);
WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
wrapped->record =
PackWindowHandleInRecord(WindowingSystem::Wayland, (void *)(uintptr_t)pCreateInfo->surface);
Keyboard::UseWaylandDisplay(pCreateInfo->display);
Keyboard::AddInputWindow(WindowingSystem::Wayland, pCreateInfo->surface);
}
return ret;
@@ -85,7 +131,7 @@ VkBool32 WrappedVulkan::vkGetPhysicalDeviceXlibPresentationSupportKHR(
namespace Keyboard
{
void CloneDisplay(Display *dpy);
void UseXlibDisplay(Display *dpy);
}
VkResult WrappedVulkan::vkCreateXlibSurfaceKHR(VkInstance instance,
@@ -105,11 +151,12 @@ VkResult WrappedVulkan::vkCreateXlibSurfaceKHR(VkInstance instance,
WrappedVkSurfaceKHR *wrapped = GetWrapped(*pSurface);
// since there's no point in allocating a full resource record and storing the window
// handle under there somewhere, we just cast. We won't use the resource record for anything
wrapped->record = (VkResourceRecord *)pCreateInfo->window;
wrapped->record =
PackWindowHandleInRecord(WindowingSystem::Xlib, (void *)(uintptr_t)pCreateInfo->window);
Keyboard::CloneDisplay(pCreateInfo->dpy);
Keyboard::UseXlibDisplay(pCreateInfo->dpy);
Keyboard::AddInputWindow(WindowingSystem::Xlib, (void *)(uintptr_t)pCreateInfo->window);
}
return ret;
@@ -152,6 +199,15 @@ void VulkanReplay::OutputWindow::SetWindowHandle(WindowingData window)
}
#endif
#if ENABLED(RDOC_WAYLAND)
if(window.system == WindowingSystem::Wayland)
{
wayland.display = window.wayland.display;
wayland.window = window.wayland.window;
return;
}
#endif
RDCERR("Unrecognised/unsupported window system %d", window.system);
}
@@ -193,6 +249,24 @@ void VulkanReplay::OutputWindow::CreateSurface(WrappedVulkan *driver, VkInstance
}
#endif
#if ENABLED(RDOC_WAYLAND)
if(m_WindowSystem == WindowingSystem::Wayland)
{
VkWaylandSurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.display = wayland.display;
createInfo.surface = wayland.window;
VkResult vkr = ObjDisp(inst)->CreateWaylandSurfaceKHR(Unwrap(inst), &createInfo, NULL, &surface);
RDCASSERTEQUAL(vkr, VK_SUCCESS);
return;
}
#endif
RDCERR("Unrecognised/unsupported window system %d", m_WindowSystem);
}
@@ -239,6 +313,17 @@ void VulkanReplay::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h
}
#endif
#if ENABLED(RDOC_WAYLAND)
if(outw.m_WindowSystem == WindowingSystem::Wayland)
{
RDCWARN("Need Wayland query for current surface dimensions");
w = RDCMAX(1U, outw.width);
h = RDCMAX(1U, outw.height);
return;
}
#endif
RDCERR("Unrecognised/unsupported window system %d", outw.m_WindowSystem);
}
+24 -3
View File
@@ -52,9 +52,10 @@ void WrappedVulkan::AddRequiredExtensions(bool instance, std::vector<std::string
// check if our compile-time options expect any WSI to be available, or if it's all disabled
#define EXPECT_WSI 0
#if(defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) || \
defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_MACOS_MVK) || \
defined(VK_USE_PLATFORM_METAL_EXT) || defined(VK_USE_PLATFORM_GGP))
#if(defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) || \
defined(VK_USE_PLATFORM_WAYLAND_KHR) || defined(VK_USE_PLATFORM_XLIB_KHR) || \
defined(VK_USE_PLATFORM_MACOS_MVK) || defined(VK_USE_PLATFORM_METAL_EXT) || \
defined(VK_USE_PLATFORM_GGP))
#undef EXPECT_WSI
#define EXPECT_WSI 1
@@ -68,6 +69,21 @@ void WrappedVulkan::AddRequiredExtensions(bool instance, std::vector<std::string
extensionList.end())
extensionList.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
// check if supported
if(supportedExtensions.find(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME) != supportedExtensions.end())
{
m_SupportedWindowSystems.push_back(WindowingSystem::Wayland);
// don't add duplicates
if(std::find(extensionList.begin(), extensionList.end(),
VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME) == extensionList.end())
{
extensionList.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
}
}
#endif
#if defined(VK_USE_PLATFORM_XCB_KHR)
// check if supported
if(supportedExtensions.find(VK_KHR_XCB_SURFACE_EXTENSION_NAME) != supportedExtensions.end())
@@ -198,6 +214,11 @@ void WrappedVulkan::AddRequiredExtensions(bool instance, std::vector<std::string
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
RDCWARN("Wayland Output requires the '%s' extension to be present",
VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
#endif
#if defined(VK_USE_PLATFORM_XCB_KHR)
RDCWARN("XCB Output requires the '%s' extension to be present",
VK_KHR_XCB_SURFACE_EXTENSION_NAME);
+22 -2
View File
@@ -79,13 +79,33 @@
#endif
#if ENABLED(RDOC_WAYLAND)
#define WINDOW_HANDLE_WAYLAND \
struct \
{ \
wl_display *display; \
wl_surface *window; \
} wayland;
#else
#define WINDOW_HANDLE_WAYLAND \
struct \
{ \
} wayland;
#endif
#define WINDOW_HANDLE_DECL \
WINDOW_HANDLE_XLIB \
WINDOW_HANDLE_XCB
WINDOW_HANDLE_XCB \
WINDOW_HANDLE_WAYLAND
#define WINDOW_HANDLE_INIT \
RDCEraseEl(xlib); \
RDCEraseEl(xcb);
RDCEraseEl(xcb); \
RDCEraseEl(wayland);
#elif ENABLED(RDOC_APPLE) || ENABLED(RDOC_GGP)
+2 -2
View File
@@ -154,9 +154,9 @@ VkResult WrappedVulkan::vkCreateWin32SurfaceKHR(VkInstance instance,
// since there's no point in allocating a full resource record and storing the window
// handle under there somewhere, we just cast. We won't use the resource record for anything
wrapped->record = (VkResourceRecord *)pCreateInfo->hwnd;
wrapped->record = PackWindowHandleInRecord(WindowingSystem::Win32, (void *)pCreateInfo->hwnd);
Keyboard::AddInputWindow((void *)pCreateInfo->hwnd);
Keyboard::AddInputWindow(WindowingSystem::Win32, (void *)pCreateInfo->hwnd);
}
return ret;
@@ -491,7 +491,7 @@ void WrappedVulkan::WrapAndProcessCreatedSwapchain(VkDevice device,
SwapchainInfo &swapInfo = *record->swapInfo;
// sneaky casting of window handle into record
swapInfo.wndHandle = (RENDERDOC_WindowHandle)GetRecord(pCreateInfo->surface);
swapInfo.wndHandle = ((PackedWindowHandle *)GetRecord(pCreateInfo->surface))->handle;
{
SCOPED_LOCK(m_SwapLookupLock);
@@ -846,7 +846,11 @@ void WrappedVulkan::vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surfac
// record pointer has window handle packed in
if(wrapper->record)
Keyboard::RemoveInputWindow((void *)wrapper->record);
{
PackedWindowHandle *wnd = (PackedWindowHandle *)wrapper->record;
Keyboard::RemoveInputWindow(wnd->system, wnd->handle);
delete wnd;
}
// now set record pointer back to NULL so no-one tries to delete it
wrapper->record = NULL;
+2 -2
View File
@@ -356,8 +356,8 @@ inline bool slurp(const char *filename, std::vector<unsigned char> &buffer)
namespace Keyboard
{
void Init();
void AddInputWindow(void *wnd);
void RemoveInputWindow(void *wnd);
void AddInputWindow(WindowingSystem windowSystem, void *wnd);
void RemoveInputWindow(WindowingSystem windowSystem, void *wnd);
bool GetKeyState(int key);
bool PlatformHasKeyInput();
};
@@ -40,11 +40,11 @@ bool PlatformHasKeyInput()
return false;
}
void AddInputWindow(void *wnd)
void AddInputWindow(WindowingSystem windowSystem, void *wnd)
{
}
void RemoveInputWindow(void *wnd)
void RemoveInputWindow(WindowingSystem windowSystem, void *wnd)
{
}
+2 -2
View File
@@ -45,11 +45,11 @@ bool PlatformHasKeyInput()
return false;
}
void AddInputWindow(void *wnd)
void AddInputWindow(WindowingSystem windowSystem, void *wnd)
{
}
void RemoveInputWindow(void *wnd)
void RemoveInputWindow(WindowingSystem windowSystem, void *wnd)
{
}
+2 -2
View File
@@ -50,11 +50,11 @@ bool PlatformHasKeyInput()
return false;
}
void AddInputWindow(void *wnd)
void AddInputWindow(WindowingSystem windowSystem, void *wnd)
{
}
void RemoveInputWindow(void *wnd)
void RemoveInputWindow(WindowingSystem windowSystem, void *wnd)
{
}
+298 -8
View File
@@ -32,6 +32,7 @@
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <set>
#include "api/app/renderdoc_app.h"
#include "common/threading.h"
#include "os/os_specific.h"
@@ -47,6 +48,11 @@
#include <xcb/xcb_keysyms.h>
#endif
#if ENABLED(RDOC_WAYLAND)
#include <linux/input.h>
#include <wayland-client.h>
#endif
namespace Keyboard
{
void Init()
@@ -55,7 +61,7 @@ void Init()
bool PlatformHasKeyInput()
{
#if ENABLED(RDOC_XCB) || ENABLED(RDOC_XLIB)
#if ENABLED(RDOC_XCB) || ENABLED(RDOC_XLIB) || ENABLED(RDOC_WAYLAND)
return true;
#else
return false;
@@ -66,7 +72,7 @@ bool PlatformHasKeyInput()
Display *CurrentXDisplay = NULL;
void CloneDisplay(Display *dpy)
void UseXlibDisplay(Display *dpy)
{
if(CurrentXDisplay || dpy == NULL)
return;
@@ -137,7 +143,7 @@ bool GetXlibKeyState(int key)
// if RENDERDOC_WINDOWING_XLIB is not enabled
void CloneDisplay(Display *dpy)
void UseXlibDisplay(Display *dpy)
{
}
@@ -153,7 +159,7 @@ bool GetXlibKeyState(int key)
xcb_connection_t *connection;
xcb_key_symbols_t *symbols;
void UseConnection(xcb_connection_t *conn)
void UseXcbConnection(xcb_connection_t *conn)
{
connection = conn;
symbols = xcb_key_symbols_alloc(conn);
@@ -233,6 +239,10 @@ bool GetXCBKeyState(int key)
// if RENDERDOC_WINDOWING_XCB is not enabled
void UseXcbConnection(xcb_connection_t *conn)
{
}
bool GetXCBKeyState(int key)
{
return false;
@@ -240,18 +250,298 @@ bool GetXCBKeyState(int key)
#endif
void AddInputWindow(void *wnd)
#if ENABLED(RDOC_WAYLAND)
#include <wayland-client.h>
std::set<wl_display *> displays;
std::set<wl_surface *> surfaces;
std::map<rdcpair<wl_registry *, uint32_t>, wl_seat *> seatNames;
std::map<wl_seat *, wl_keyboard *> seatKeyboard;
bool inFocus = false;
Threading::CriticalSection waylandLock;
bool keyState[eRENDERDOC_Key_Max] = {};
void WaylandKeymapDummy(void *data, wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size)
{
// TODO check against this drawable & parent window being focused in GetKeyState
}
void RemoveInputWindow(void *wnd)
void WaylandModifiersDummy(void *data, wl_keyboard *keyboard, uint32_t serial,
uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
uint32_t group)
{
}
void WaylandRepeatInfoDummy(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay)
{
}
void WaylandEnter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surf, wl_array *keys)
{
SCOPED_LOCK(waylandLock);
inFocus = surfaces.find(surf) != surfaces.end();
RDCEraseEl(keyState);
}
void WaylandLeave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surf)
{
SCOPED_LOCK(waylandLock);
inFocus = false;
RDCEraseEl(keyState);
}
void WaylandKeypress(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time,
uint32_t key, uint32_t state)
{
int keyIdx = -1;
switch(key)
{
case KEY_0: keyIdx = eRENDERDOC_Key_0; break;
case KEY_1: keyIdx = eRENDERDOC_Key_1; break;
case KEY_2: keyIdx = eRENDERDOC_Key_2; break;
case KEY_3: keyIdx = eRENDERDOC_Key_3; break;
case KEY_4: keyIdx = eRENDERDOC_Key_4; break;
case KEY_5: keyIdx = eRENDERDOC_Key_5; break;
case KEY_6: keyIdx = eRENDERDOC_Key_6; break;
case KEY_7: keyIdx = eRENDERDOC_Key_7; break;
case KEY_8: keyIdx = eRENDERDOC_Key_8; break;
case KEY_9: keyIdx = eRENDERDOC_Key_9; break;
case KEY_A: keyIdx = eRENDERDOC_Key_A; break;
case KEY_B: keyIdx = eRENDERDOC_Key_B; break;
case KEY_C: keyIdx = eRENDERDOC_Key_C; break;
case KEY_D: keyIdx = eRENDERDOC_Key_D; break;
case KEY_E: keyIdx = eRENDERDOC_Key_E; break;
case KEY_F: keyIdx = eRENDERDOC_Key_F; break;
case KEY_G: keyIdx = eRENDERDOC_Key_G; break;
case KEY_H: keyIdx = eRENDERDOC_Key_H; break;
case KEY_I: keyIdx = eRENDERDOC_Key_I; break;
case KEY_J: keyIdx = eRENDERDOC_Key_J; break;
case KEY_K: keyIdx = eRENDERDOC_Key_K; break;
case KEY_L: keyIdx = eRENDERDOC_Key_L; break;
case KEY_M: keyIdx = eRENDERDOC_Key_M; break;
case KEY_N: keyIdx = eRENDERDOC_Key_N; break;
case KEY_O: keyIdx = eRENDERDOC_Key_O; break;
case KEY_P: keyIdx = eRENDERDOC_Key_P; break;
case KEY_Q: keyIdx = eRENDERDOC_Key_Q; break;
case KEY_R: keyIdx = eRENDERDOC_Key_R; break;
case KEY_S: keyIdx = eRENDERDOC_Key_S; break;
case KEY_T: keyIdx = eRENDERDOC_Key_T; break;
case KEY_U: keyIdx = eRENDERDOC_Key_U; break;
case KEY_V: keyIdx = eRENDERDOC_Key_V; break;
case KEY_W: keyIdx = eRENDERDOC_Key_W; break;
case KEY_X: keyIdx = eRENDERDOC_Key_X; break;
case KEY_Y: keyIdx = eRENDERDOC_Key_Y; break;
case KEY_Z: keyIdx = eRENDERDOC_Key_Z; break;
case KEY_KPSLASH: keyIdx = eRENDERDOC_Key_Divide; break;
case KEY_KPASTERISK: keyIdx = eRENDERDOC_Key_Multiply; break;
case KEY_KPMINUS: keyIdx = eRENDERDOC_Key_Subtract; break;
case KEY_KPPLUS: keyIdx = eRENDERDOC_Key_Plus; break;
case KEY_F1: keyIdx = eRENDERDOC_Key_F1; break;
case KEY_F2: keyIdx = eRENDERDOC_Key_F2; break;
case KEY_F3: keyIdx = eRENDERDOC_Key_F3; break;
case KEY_F4: keyIdx = eRENDERDOC_Key_F4; break;
case KEY_F5: keyIdx = eRENDERDOC_Key_F5; break;
case KEY_F6: keyIdx = eRENDERDOC_Key_F6; break;
case KEY_F7: keyIdx = eRENDERDOC_Key_F7; break;
case KEY_F8: keyIdx = eRENDERDOC_Key_F8; break;
case KEY_F9: keyIdx = eRENDERDOC_Key_F9; break;
case KEY_F10: keyIdx = eRENDERDOC_Key_F10; break;
case KEY_F11: keyIdx = eRENDERDOC_Key_F11; break;
case KEY_F12: keyIdx = eRENDERDOC_Key_F12; break;
case KEY_HOME: keyIdx = eRENDERDOC_Key_Home; break;
case KEY_END: keyIdx = eRENDERDOC_Key_End; break;
case KEY_INSERT: keyIdx = eRENDERDOC_Key_Insert; break;
case KEY_DELETE: keyIdx = eRENDERDOC_Key_Delete; break;
case KEY_PAGEUP: keyIdx = eRENDERDOC_Key_PageUp; break;
case KEY_PAGEDOWN: keyIdx = eRENDERDOC_Key_PageDn; break;
case KEY_BACKSPACE: keyIdx = eRENDERDOC_Key_Backspace; break;
case KEY_TAB: keyIdx = eRENDERDOC_Key_Tab; break;
case KEY_SYSRQ: keyIdx = eRENDERDOC_Key_PrtScrn; break;
case KEY_PAUSE: keyIdx = eRENDERDOC_Key_Pause; break;
}
if(keyIdx < 0)
return;
{
SCOPED_LOCK(waylandLock);
keyState[keyIdx] = (state == WL_KEYBOARD_KEY_STATE_PRESSED);
}
}
void WaylandSeatCaps(void *data, wl_seat *seat, uint32_t capabilities)
{
if(capabilities & WL_SEAT_CAPABILITY_KEYBOARD)
{
{
SCOPED_LOCK(waylandLock);
if(seatKeyboard[seat])
return;
}
wl_keyboard *keyboard = wl_seat_get_keyboard(seat);
static const wl_keyboard_listener listener = {
WaylandKeymapDummy, WaylandEnter, WaylandLeave,
WaylandKeypress, WaylandModifiersDummy, WaylandRepeatInfoDummy,
};
wl_keyboard_add_listener(keyboard, &listener, NULL);
{
SCOPED_LOCK(waylandLock);
seatKeyboard[seat] = keyboard;
}
}
else
{
wl_keyboard *keyboard = NULL;
{
SCOPED_LOCK(waylandLock);
keyboard = seatKeyboard[seat];
if(!keyboard)
return;
seatKeyboard[seat] = NULL;
}
wl_keyboard_destroy(keyboard);
}
}
void WaylandRegistryAdd(void *data, wl_registry *reg, uint32_t name, const char *iface,
uint32_t version)
{
if(!strcmp(iface, "wl_seat"))
{
wl_seat *seat = (wl_seat *)wl_registry_bind(reg, name, &wl_seat_interface, 1);
static const wl_seat_listener listener = {&WaylandSeatCaps};
wl_seat_add_listener(seat, &listener, NULL);
{
SCOPED_LOCK(waylandLock);
seatNames[{reg, name}] = seat;
}
}
}
void WaylandRegistryRemove(void *data, wl_registry *reg, uint32_t name)
{
SCOPED_LOCK(waylandLock);
auto it = seatNames.find({reg, name});
if(it != seatNames.end())
{
wl_seat_destroy(it->second);
seatNames.erase(it);
}
}
void UseWaylandDisplay(wl_display *disp)
{
// only listen to each display once at most
{
SCOPED_LOCK(waylandLock);
if(displays.find(disp) != displays.end())
return;
displays.insert(disp);
}
static const wl_registry_listener listener = {&WaylandRegistryAdd, &WaylandRegistryRemove};
// get the registry and listen to it. This will then let us find seats
wl_registry_add_listener(wl_display_get_registry(disp), &listener, NULL);
}
void AddWaylandInputWindow(wl_surface *wnd)
{
SCOPED_LOCK(waylandLock);
surfaces.insert(wnd);
}
void RemoveWaylandInputWindow(wl_surface *wnd)
{
SCOPED_LOCK(waylandLock);
surfaces.erase(wnd);
}
bool GetWaylandKeyState(int key)
{
SCOPED_LOCK(waylandLock);
return keyState[key];
}
#else
void UseWaylandDisplay(wl_display *disp)
{
}
void AddWaylandInputWindow(wl_surface *wnd)
{
}
void RemoveWaylandInputWindow(wl_surface *wnd)
{
}
bool GetWaylandKeyState(int key)
{
return false;
}
#endif
WindowingSystem UseUnknownDisplay(void *disp)
{
// could be wayland or xlib, try to detect.
// both Display* and wl_display* are valid pointers, so dereference and read the first pointer
// sized bytes
void *firstPointer = NULL;
memcpy(&firstPointer, disp, sizeof(void *));
// in a Display* we don't know what this contains, but in a wl_display it should point to the
// wl_display_interface exported symbol. Check with dladdr
Dl_info info;
if(dladdr(firstPointer, &info) && !strcmp(info.dli_sname, "wl_display_interface"))
{
UseWaylandDisplay((wl_display *)disp);
return WindowingSystem::Wayland;
}
else
{
UseXlibDisplay((Display *)disp);
return WindowingSystem::Xlib;
}
}
void AddInputWindow(WindowingSystem windowSystem, void *wnd)
{
if(windowSystem == WindowingSystem::Wayland)
{
AddWaylandInputWindow((wl_surface *)wnd);
}
else
{
// TODO check against this drawable & parent window being focused in GetKeyState
}
}
void RemoveInputWindow(WindowingSystem windowSystem, void *wnd)
{
if(windowSystem == WindowingSystem::Wayland)
{
RemoveWaylandInputWindow((wl_surface *)wnd);
}
}
bool GetKeyState(int key)
{
return GetXCBKeyState(key) || GetXlibKeyState(key);
return GetXCBKeyState(key) || GetXlibKeyState(key) || GetWaylandKeyState(key);
}
}
+4 -2
View File
@@ -79,13 +79,15 @@ bool PlatformHasKeyInput()
std::set<HWND> inputWindows;
void AddInputWindow(void *wnd)
void AddInputWindow(WindowingSystem windowSystem, void *wnd)
{
RDCASSERTEQUAL(windowSystem, WindowingSystem::Win32);
inputWindows.insert((HWND)wnd);
}
void RemoveInputWindow(void *wnd)
void RemoveInputWindow(WindowingSystem windowSystem, void *wnd)
{
RDCASSERTEQUAL(windowSystem, WindowingSystem::Win32);
inputWindows.erase((HWND)wnd);
}
+10 -1
View File
@@ -51,7 +51,16 @@ static uint64_t GetHandle(WindowingData window)
#endif
}
RDCERR("Unrecognised window system %d", system);
if(window.system == WindowingSystem::Wayland)
{
#if ENABLED(RDOC_WAYLAND)
return (uint64_t)window.wayland.window;
#else
RDCERR("Wayland windowing system data passed in, but support is not compiled in");
#endif
}
RDCERR("Unrecognised window system %s", ToStr(window.system).c_str());
return 0;
+5
View File
@@ -537,6 +537,11 @@ int main(int argc, char *argv[])
count++;
#endif
#if defined(RENDERDOC_WINDOWING_WAYLAND)
support += "Wayland (CAPTURE ONLY), ";
count++;
#endif
#if defined(RENDERDOC_SUPPORT_VULKAN)
support += "Vulkan KHR_display, ";
count++;