mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-08 11:00:29 +00:00
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:
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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); \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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++;
|
||||
|
||||
Reference in New Issue
Block a user