Implement CGLPlatform using CGL and NSOpenGLContext

* On replay on macOS we use NSOpenGLContext so we can render to windows.
* We have two windowing systems on mac - one for Metal compatible outputs and
  one for OpenGL compatible outputs.
This commit is contained in:
baldurk
2019-01-30 23:03:01 +00:00
parent 69c668c27c
commit fe0be58908
20 changed files with 375 additions and 57 deletions
+1 -1
View File
@@ -1773,7 +1773,7 @@ WindowingData CaptureContext::CreateWindowingData(QWidget *window)
void *layer = makeNSViewMetalCompatible(view);
return CreateMacOSWindowingData(layer);
return CreateMacOSWindowingData(view, layer);
#elif defined(RENDERDOC_PLATFORM_APPLE)
+10 -5
View File
@@ -331,7 +331,8 @@ DOCUMENT(R"(Specifies a windowing system to use for creating an output window.
.. data:: MacOS
The windowing data refers to a MacOS / OS X CALayer. See :func:`CreateMacOSWindowingData`.
The windowing data refers to a MacOS / OS X NSView & CALayer that is Metal/GL compatible.
See :func:`CreateMacOSWindowingData`.
)");
enum class WindowingSystem : uint32_t
{
@@ -403,6 +404,7 @@ struct WindowingData
struct
{
void *view;
void *layer;
} macOS;
};
@@ -494,18 +496,21 @@ inline const WindowingData CreateAndroidWindowingData(ANativeWindow *window)
return ret;
}
DOCUMENT(R"(Create a :class:`WindowingData` for an macOS ``CALayer`` handle (as void pointer).
DOCUMENT(R"(Create a :class:`WindowingData` for an metal/opengl-compatible macOS ``CALayer`` handle
and ``NSView`` handle (as void pointers).
:param CALayer window: The native ``CALayer`` handle for this window.
:param NSView view: The native ``NSView`` handle for this window.
:param CALayer layer: The native ``CALayer`` handle for this window.
:return: A :class:`WindowingData` corresponding to the given window.
:rtype: WindowingData
)");
inline const WindowingData CreateMacOSWindowingData(void *view)
inline const WindowingData CreateMacOSWindowingData(void *view, void *layer)
{
WindowingData ret = {};
ret.system = WindowingSystem::MacOS;
ret.macOS.layer = view;
ret.macOS.view = view;
ret.macOS.layer = layer;
return ret;
}
+15
View File
@@ -54,6 +54,21 @@ std::string DoStringise(const ReplayStatus &el)
END_ENUM_STRINGISE();
}
template <>
std::string DoStringise(const WindowingSystem &el)
{
BEGIN_ENUM_STRINGISE(WindowingSystem)
{
STRINGISE_ENUM_CLASS(Unknown);
STRINGISE_ENUM_CLASS(Win32);
STRINGISE_ENUM_CLASS(Xlib);
STRINGISE_ENUM_CLASS(XCB);
STRINGISE_ENUM_CLASS(Android);
STRINGISE_ENUM_CLASS(MacOS);
}
END_ENUM_STRINGISE();
}
template <>
std::string DoStringise(const ResourceFormatType &el)
{
-14
View File
@@ -107,20 +107,6 @@ std::string DoStringise(const VendorExtensions &el)
END_ENUM_STRINGISE();
}
template <>
std::string DoStringise(const WindowingSystem &el)
{
BEGIN_ENUM_STRINGISE(WindowingSystem);
{
STRINGISE_ENUM_CLASS(Unknown);
STRINGISE_ENUM_CLASS(Win32);
STRINGISE_ENUM_CLASS(Xlib);
STRINGISE_ENUM_CLASS(XCB);
STRINGISE_ENUM_CLASS(Android);
}
END_ENUM_STRINGISE();
}
template <>
std::string DoStringise(const RENDERDOC_InputButton &el)
{
+1
View File
@@ -54,6 +54,7 @@ list(APPEND sources gl_hooks.cpp)
if(APPLE)
list(APPEND sources
cgl_dispatch_table.h
cgl_platform.mm
cgl_platform.cpp
cgl_hooks.cpp)
else()
+6 -1
View File
@@ -41,6 +41,9 @@ typedef CGLError (*PFN_CGLGetSurface)(CGLContextObj gl, CGSConnectionID *cid, CG
CGSSurfaceID *sid);
typedef CGLError (*PFN_CGSGetSurfaceBounds)(CGSConnectionID cid, CGSWindowID wid, CGSSurfaceID sid,
CGRect *rect);
typedef CGLError (*PFN_CGLChoosePixelFormat)(const CGLPixelFormatAttribute *attribs,
CGLPixelFormatObj *pix, GLint *npix);
typedef CGLError (*PFN_CGLDestroyPixelFormat)(CGLPixelFormatObj pix);
#define CGL_HOOKED_SYMBOLS(FUNC) \
FUNC(CGLCreateContext); \
@@ -52,7 +55,9 @@ typedef CGLError (*PFN_CGSGetSurfaceBounds)(CGSConnectionID cid, CGSWindowID wid
FUNC(CGLDescribePixelFormat); \
FUNC(CGLSetSurface); \
FUNC(CGLGetSurface); \
FUNC(CGSGetSurfaceBounds);
FUNC(CGSGetSurfaceBounds); \
FUNC(CGLChoosePixelFormat); \
FUNC(CGLDestroyPixelFormat);
struct CGLDispatchTable
{
+2 -2
View File
@@ -80,7 +80,7 @@ CGLError GL_EXPORT_NAME(CGLCreateContext)(CGLPixelFormatObj pix, CGLContextObj s
GLWindowingData data;
data.wnd = NULL;
data.ctx = *ctx;
data.cfg = pix;
data.pix = pix;
{
SCOPED_LOCK(glLock);
@@ -128,7 +128,7 @@ CGLError GL_EXPORT_NAME(CGLSetCurrentContext)(CGLContextObj ctx)
GLWindowingData data;
data.wnd = NULL;
data.ctx = ctx;
data.cfg = NULL;
data.pix = CGLGetPixelFormat(ctx);
if(data.ctx)
{
+175 -13
View File
@@ -22,38 +22,180 @@
* THE SOFTWARE.
******************************************************************************/
#define GL_GLEXT_PROTOTYPES
#include "cgl_dispatch_table.h"
#include "gl_common.h"
#include "apple_gl_hook_defs.h"
// helpers defined in cgl_platform.mm
extern "C" int NSGL_getLayerWidth(void *layer);
extern "C" int NSGL_getLayerHeight(void *layer);
extern "C" void *NSGL_createContext(void *view, void *shareNSCtx);
extern "C" void NSGL_makeCurrentContext(void *nsctx);
extern "C" void NSGL_update(void *nsctx);
extern "C" void NSGL_flushBuffer(void *nsctx);
extern "C" void NSGL_destroyContext(void *nsctx);
template <>
std::string DoStringise(const CGLError &el)
{
BEGIN_ENUM_STRINGISE(CGLError);
{
STRINGISE_ENUM_NAMED(kCGLNoError, "no error");
STRINGISE_ENUM_NAMED(kCGLBadAttribute, "invalid pixel format attribute");
STRINGISE_ENUM_NAMED(kCGLBadProperty, "invalid renderer property");
STRINGISE_ENUM_NAMED(kCGLBadPixelFormat, "invalid pixel format");
STRINGISE_ENUM_NAMED(kCGLBadRendererInfo, "invalid renderer info");
STRINGISE_ENUM_NAMED(kCGLBadContext, "invalid context");
STRINGISE_ENUM_NAMED(kCGLBadDrawable, "invalid drawable");
STRINGISE_ENUM_NAMED(kCGLBadDisplay, "invalid graphics device");
STRINGISE_ENUM_NAMED(kCGLBadState, "invalid context state");
STRINGISE_ENUM_NAMED(kCGLBadValue, "invalid numerical value");
STRINGISE_ENUM_NAMED(kCGLBadMatch, "invalid share context");
STRINGISE_ENUM_NAMED(kCGLBadEnumeration, "invalid enumerant");
STRINGISE_ENUM_NAMED(kCGLBadOffScreen, "invalid offscreen drawable");
STRINGISE_ENUM_NAMED(kCGLBadFullScreen, "invalid fullscreen drawable");
STRINGISE_ENUM_NAMED(kCGLBadWindow, "invalid window");
STRINGISE_ENUM_NAMED(kCGLBadAddress, "invalid pointer");
STRINGISE_ENUM_NAMED(kCGLBadCodeModule, "invalid code module");
STRINGISE_ENUM_NAMED(kCGLBadAlloc, "invalid memory allocation");
STRINGISE_ENUM_NAMED(kCGLBadConnection, "invalid CoreGraphics connection");
}
END_ENUM_STRINGISE();
}
class CGLPlatform : public GLPlatform
{
bool MakeContextCurrent(GLWindowingData data) { return false; }
bool MakeContextCurrent(GLWindowingData data)
{
if(RenderDoc::Inst().IsReplayApp())
{
NSGL_makeCurrentContext(data.nsctx);
return true;
}
else
{
if(CGL.CGLSetCurrentContext)
{
CGLError err = CGL.CGLSetCurrentContext(data.ctx);
if(err == kCGLNoError)
return true;
RDCERR("MakeContextCurrent: %s", ToStr(err).c_str());
}
}
return false;
}
GLWindowingData CloneTemporaryContext(GLWindowingData share)
{
GLWindowingData ret;
GLWindowingData ret = share;
ret.ctx = NULL;
if(RenderDoc::Inst().IsReplayApp())
{
RDCASSERT(share.nsctx);
ret.nsctx = NSGL_createContext(NULL, share.nsctx);
}
else
{
if(share.ctx && CGL.CGLCreateContext)
{
CGLError err = CGL.CGLCreateContext(share.pix, share.ctx, &ret.ctx);
RDCASSERTMSG("Error creating temporary context", err != kCGLNoError, err);
}
}
return ret;
}
void DeleteClonedContext(GLWindowingData context) {}
void DeleteReplayContext(GLWindowingData context) {}
void SwapBuffers(GLWindowingData context) {}
void GetOutputWindowDimensions(GLWindowingData context, int32_t &w, int32_t &h) { w = h = 0; }
bool IsOutputWindowVisible(GLWindowingData context) { return false; }
void DeleteClonedContext(GLWindowingData context)
{
if(RenderDoc::Inst().IsReplayApp())
{
NSGL_destroyContext(context.nsctx);
}
else
{
if(context.ctx && CGL.CGLDestroyContext)
CGL.CGLDestroyContext(context.ctx);
}
}
void DeleteReplayContext(GLWindowingData context)
{
RDCASSERT(context.nsctx);
NSGL_destroyContext(context.nsctx);
}
void SwapBuffers(GLWindowingData context) { NSGL_flushBuffer(context.nsctx); }
void WindowResized(GLWindowingData context) { NSGL_update(context.nsctx); }
void GetOutputWindowDimensions(GLWindowingData context, int32_t &w, int32_t &h)
{
if(context.layer)
{
w = NSGL_getLayerWidth(context.layer);
h = NSGL_getLayerHeight(context.layer);
}
else
{
w = h = 0;
}
}
bool IsOutputWindowVisible(GLWindowingData context) { return true; }
void *GetReplayFunction(const char *funcname)
{
#undef APPLE_FUNC
#define APPLE_FUNC(function) \
if(!strcmp(funcname, STRINGIZE(function))) \
return (void *)&::function;
ForEachAppleSupported();
return NULL;
}
bool CanCreateGLESContext() { return false; }
bool PopulateForReplay() { return CGL.PopulateForReplay(); }
GLWindowingData MakeOutputWindow(WindowingData window, bool depth, GLWindowingData share_context)
{
GLWindowingData ret = {};
if(window.system == WindowingSystem::MacOS)
{
RDCASSERT(window.macOS.layer && window.macOS.view);
ret.nsctx = NSGL_createContext(window.macOS.view, share_context.nsctx);
ret.wnd = window.macOS.view;
ret.layer = window.macOS.layer;
return ret;
}
else if(window.system == WindowingSystem::Unknown)
{
ret.nsctx = NSGL_createContext(NULL, share_context.nsctx);
return ret;
}
else
{
RDCERR("Unexpected window system %u", system);
}
return ret;
}
void *GetReplayFunction(const char *funcname) { return NULL; }
bool CanCreateGLESContext() { return false; }
bool PopulateForReplay() { return false; }
ReplayStatus InitialiseAPI(GLWindowingData &replayContext, RDCDriver api)
{
return ReplayStatus::APIUnsupported;
RDCASSERT(api == RDCDriver::OpenGL);
replayContext.nsctx = NSGL_createContext(NULL, NULL);
return ReplayStatus::Succeeded;
}
void DrawQuads(float width, float height, const std::vector<Vec4f> &vertices) {}
void DrawQuads(float width, float height, const std::vector<Vec4f> &vertices)
{
RDCERR("Legacy overlay not supported on macOS");
}
} cglPlatform;
CGLDispatchTable CGL = {};
@@ -65,5 +207,25 @@ GLPlatform &GetGLPlatform()
bool CGLDispatchTable::PopulateForReplay()
{
return false;
RDCASSERT(RenderDoc::Inst().IsReplayApp());
RDCDEBUG("Initialising GL function pointers");
bool symbols_ok = true;
#define LOAD_FUNC(func) \
if(!this->func) \
this->func = &::func; \
\
if(!this->func) \
{ \
symbols_ok = false; \
RDCWARN("Unable to load '%s'", STRINGIZE(func)); \
}
CGL_HOOKED_SYMBOLS(LOAD_FUNC)
CGL_NONHOOKED_SYMBOLS(LOAD_FUNC)
#undef LOAD_FUNC
return symbols_ok;
}
+108
View File
@@ -0,0 +1,108 @@
#import <Cocoa/Cocoa.h>
extern "C" void RENDERDOC_LogText(const char *text);
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
extern "C" int NSGL_getLayerWidth(void *handle)
{
CALayer *layer = (CALayer *)handle;
assert([layer isKindOfClass:[CALayer class]]);
return layer.bounds.size.width;
}
extern "C" int NSGL_getLayerHeight(void *handle)
{
CALayer *layer = (CALayer *)handle;
assert([layer isKindOfClass:[CALayer class]]);
return layer.bounds.size.height;
}
extern "C" void *NSGL_createContext(void *handle, void *sharehandle)
{
NSView *view = (NSView *)handle;
assert(view == nil || [view isKindOfClass:[NSView class]]);
NSOpenGLContext *share = (NSOpenGLContext *)sharehandle;
assert(share == nil || [share isKindOfClass:[NSOpenGLContext class]]);
NSOpenGLPixelFormatAttribute attr[] = {
NSOpenGLPFANoRecovery,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
NSOpenGLPFAAllowOfflineRenderers,
NSOpenGLPFAOpenGLProfile,
NSOpenGLProfileVersion4_1Core,
NSOpenGLPFAColorSize,
32,
0,
};
NSOpenGLPixelFormat *pix = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
if(pix == nil)
{
RENDERDOC_LogText("Failed to create NSOpenGLPixelFormat");
return nil;
}
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pix shareContext:share];
[pix release];
if(context == nil)
{
RENDERDOC_LogText("Failed to create NSOpenGLContext");
return nil;
}
GLint aboveWindow = 1;
[context setValues:&aboveWindow forParameter:NSOpenGLCPSurfaceOrder];
[context setView:view];
[context update];
return context;
}
extern "C" void NSGL_makeCurrentContext(void *handle)
{
NSOpenGLContext *context = (NSOpenGLContext *)handle;
assert([context isKindOfClass:[NSOpenGLContext class]]);
[context makeCurrentContext];
}
extern "C" void NSGL_update(void *handle)
{
NSOpenGLContext *context = (NSOpenGLContext *)handle;
assert([context isKindOfClass:[NSOpenGLContext class]]);
[context update];
}
extern "C" void NSGL_flushBuffer(void *handle)
{
NSOpenGLContext *context = (NSOpenGLContext *)handle;
assert([context isKindOfClass:[NSOpenGLContext class]]);
[context flushBuffer];
}
extern "C" void NSGL_destroyContext(void *handle)
{
@autoreleasepool
{
NSOpenGLContext *context = (NSOpenGLContext *)handle;
assert([context isKindOfClass:[NSOpenGLContext class]]);
[context makeCurrentContext];
[context clearDrawable];
[context update];
[context release];
}
}
+1
View File
@@ -84,6 +84,7 @@ class EGLPlatform : public GLPlatform
}
void SwapBuffers(GLWindowingData context) { EGL.SwapBuffers(context.egl_dpy, context.egl_wnd); }
void WindowResized(GLWindowingData context) {}
void GetOutputWindowDimensions(GLWindowingData context, int32_t &w, int32_t &h)
{
// On some Linux systems the surface seems to be context dependant.
+14 -4
View File
@@ -204,12 +204,21 @@ struct GLWindowingData
{
ctx = NULL;
wnd = NULL;
cfg = NULL;
pix = NULL;
layer = NULL;
}
CGLContextObj ctx;
void *wnd;
CGLPixelFormatObj cfg;
union
{
CGLContextObj ctx;
void *nsctx; // during replay only, this is the NSOpenGLContext
};
void *wnd; // during capture, this is the CGL window ID. During replay, it's the NSView
CGLPixelFormatObj pix;
void *layer; // during replay only, this is the CALayer
};
#define DECL_HOOK_EXPORT(function) \
@@ -268,6 +277,7 @@ struct GLPlatform
virtual void DeleteReplayContext(GLWindowingData context) = 0;
virtual bool MakeContextCurrent(GLWindowingData data) = 0;
virtual void SwapBuffers(GLWindowingData context) = 0;
virtual void WindowResized(GLWindowingData context) = 0;
virtual void GetOutputWindowDimensions(GLWindowingData context, int32_t &w, int32_t &h) = 0;
virtual bool IsOutputWindowVisible(GLWindowingData context) = 0;
virtual GLWindowingData MakeOutputWindow(WindowingData window, bool depth,
+4
View File
@@ -111,6 +111,10 @@ bool GLReplay::CheckResizeOutputWindow(uint64_t id)
outw.width = w;
outw.height = h;
MakeCurrentReplayContext(&outw);
m_pDriver->m_Platform.WindowResized(outw);
MakeCurrentReplayContext(m_DebugCtx);
WrappedOpenGL &drv = *m_pDriver;
+21
View File
@@ -135,6 +135,26 @@ vector<uint32_t> GLReplay::GetPassEvents(uint32_t eventId)
return passEvents;
}
vector<WindowingSystem> GLReplay::GetSupportedWindowSystems()
{
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);
#elif ENABLED(RDOC_ANDROID)
ret.push_back(WindowingSystem::Android);
#elif ENABLED(RDOC_APPLE)
ret.push_back(WindowingSystem::MacOS);
#endif
return ret;
}
FrameRecord GLReplay::GetFrameRecord()
{
return m_pDriver->GetFrameRecord();
@@ -3474,6 +3494,7 @@ class GLDummyPlatform : public GLPlatform
virtual void DeleteReplayContext(GLWindowingData context) {}
virtual bool MakeContextCurrent(GLWindowingData data) { return true; }
virtual void SwapBuffers(GLWindowingData context) {}
virtual void WindowResized(GLWindowingData context) {}
virtual void GetOutputWindowDimensions(GLWindowingData context, int32_t &w, int32_t &h) {}
virtual bool IsOutputWindowVisible(GLWindowingData context) { return false; }
virtual GLWindowingData MakeOutputWindow(WindowingData window, bool depth,
+1 -11
View File
@@ -130,17 +130,7 @@ public:
vector<uint32_t> GetPassEvents(uint32_t eventId);
vector<WindowingSystem> GetSupportedWindowSystems()
{
vector<WindowingSystem> ret;
// 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);
return ret;
}
vector<WindowingSystem> GetSupportedWindowSystems();
AMDRGPControl *GetRGPControl() { return NULL; }
uint64_t MakeOutputWindow(WindowingData window, bool depth);
+1
View File
@@ -110,6 +110,7 @@ class GLXPlatform : public GLPlatform
}
void SwapBuffers(GLWindowingData context) { GLX.glXSwapBuffers(context.dpy, context.wnd); }
void WindowResized(GLWindowingData context) {}
void GetOutputWindowDimensions(GLWindowingData context, int32_t &w, int32_t &h)
{
GLX.glXQueryDrawable(context.dpy, context.wnd, GLX_WIDTH, (unsigned int *)&w);
+3
View File
@@ -195,6 +195,9 @@
<UseLibraryDependencyInputs>false</UseLibraryDependencyInputs>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="cgl_platform.mm" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
@@ -261,4 +261,9 @@
<Filter>Platform Interfaces\Linux</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="cgl_platform.mm">
<Filter>Platform Interfaces\Apple</Filter>
</None>
</ItemGroup>
</Project>
+1
View File
@@ -86,6 +86,7 @@ class WGLPlatform : public GLPlatform
WGL.SwapBuffers(context.DC);
}
void WindowResized(GLWindowingData context) {}
void GetOutputWindowDimensions(GLWindowingData context, int32_t &w, int32_t &h)
{
RECT rect = {0};
+4 -4
View File
@@ -29,8 +29,8 @@
#include <dlfcn.h>
// helpers defined in vk_apple.mm
extern "C" int getCALayerWidth(void *handle);
extern "C" int getCALayerHeight(void *handle);
extern "C" int getMetalLayerWidth(void *handle);
extern "C" int getMetalLayerHeight(void *handle);
VkResult WrappedVulkan::vkCreateMacOSSurfaceMVK(VkInstance instance,
const VkMacOSSurfaceCreateInfoMVK *pCreateInfo,
@@ -83,8 +83,8 @@ void VulkanReplay::GetOutputWindowDimensions(uint64_t id, int32_t &w, int32_t &h
OutputWindow &outw = m_OutputWindows[id];
w = getCALayerWidth(outw.wnd);
h = getCALayerHeight(outw.wnd);
w = getMetalLayerWidth(outw.wnd);
h = getMetalLayerHeight(outw.wnd);
}
static const char *VulkanLibraryName = "libvulkan.1.dylib";
+2 -2
View File
@@ -1,6 +1,6 @@
#import <Cocoa/Cocoa.h>
extern "C" int getCALayerWidth(void *handle)
extern "C" int getMetalLayerWidth(void *handle)
{
CALayer *layer = (CALayer *)handle;
assert([layer isKindOfClass:[CALayer class]]);
@@ -8,7 +8,7 @@ extern "C" int getCALayerWidth(void *handle)
return layer.bounds.size.width;
}
extern "C" int getCALayerHeight(void *handle)
extern "C" int getMetalLayerHeight(void *handle)
{
CALayer *layer = (CALayer *)handle;
assert([layer isKindOfClass:[CALayer class]]);