[surface] pull mp_layer into the generic mg_surface_data struct, out of each backend-specific implementation

This commit is contained in:
Martin Fouilleul 2023-03-03 11:50:12 +01:00
parent ac00cc9def
commit 731e5b1ae8
12 changed files with 228 additions and 550 deletions

View File

@ -22,7 +22,6 @@
#define pipe(fds) _pipe(fds, 256, O_BINARY)
#define read _read
#define write _write
#define itoa _itoa
#define process_id HANDLE
@ -38,8 +37,11 @@
#elif OS_MACOS
#include<unistd.h>
#include<signal.h>
void spwan_child(char* program, char** argv)
#define process_id pid_t
process_id spawn_child(char* program, char** argv)
{
pid_t pid = fork();
if(!pid)
@ -48,6 +50,12 @@
execve(program, argv, envp);
assert(0);
}
return(pid);
}
void terminate_child(process_id child)
{
kill(child, SIGTERM);
}
#endif
@ -239,7 +247,7 @@ int main(int argc, char** argv)
printf("parent process created readFd %i and writeFd %i\n", fileDesc[0], fileDesc[1]);
char writeDescStr[64];
itoa(fileDesc[1], writeDescStr, 10);
snprintf(writeDescStr, 64, "%i", fileDesc[1]);
char* args[] = {"bin/example_surface_sharing", "--child", writeDescStr, 0};
process_id child = spawn_child(args[0], args);

View File

@ -35,8 +35,6 @@ typedef struct mg_egl_surface
{
mg_surface_data interface;
mp_layer layer;
EGLDisplay eglDisplay;
EGLConfig eglConfig;
EGLContext eglContext;
@ -61,7 +59,7 @@ void mg_egl_surface_destroy(mg_surface_data* interface)
eglDestroyContext(surface->eglDisplay, surface->eglContext);
eglDestroySurface(surface->eglDisplay, surface->eglSurface);
mp_layer_cleanup(&surface->layer);
mg_surface_cleanup((mg_surface_data*)surface);
free(surface);
}
@ -84,68 +82,16 @@ void mg_egl_surface_swap_interval(mg_surface_data* interface, int swap)
eglSwapInterval(surface->eglDisplay, swap);
}
vec2 mg_egl_surface_contents_scaling(mg_surface_data* interface)
void mg_egl_surface_init(mg_egl_surface* surface)
{
mg_egl_surface* surface = (mg_egl_surface*)interface;
return(mp_layer_contents_scaling(&surface->layer));
}
void* nativeLayer = surface->interface.nativeLayer((mg_surface_data*)surface);
mp_rect mg_egl_surface_get_frame(mg_surface_data* interface)
{
mg_egl_surface* surface = (mg_egl_surface*)interface;
return(mp_layer_get_frame(&surface->layer));
}
void mg_egl_surface_set_frame(mg_surface_data* interface, mp_rect frame)
{
mg_egl_surface* surface = (mg_egl_surface*)interface;
mp_layer_set_frame(&surface->layer, frame);
}
void mg_egl_surface_set_hidden(mg_surface_data* interface, bool hidden)
{
mg_egl_surface* surface = (mg_egl_surface*)interface;
mp_layer_set_hidden(&surface->layer, hidden);
}
bool mg_egl_surface_get_hidden(mg_surface_data* interface)
{
mg_egl_surface* surface = (mg_egl_surface*)interface;
return(mp_layer_get_hidden(&surface->layer));
}
void* mg_egl_surface_native_layer(mg_surface_data* interface)
{
mg_egl_surface* surface = (mg_egl_surface*)interface;
return(mp_layer_native_surface(&surface->layer));
}
mg_surface_id mg_egl_surface_remote_id(mg_surface_data* interface)
{
mg_egl_surface* surface = (mg_egl_surface*)interface;
//////////WARN: not portable, just for testing
return((mg_surface_id)surface->layer.hWnd);
}
//////////////////////////
// PIGMODE
//////////////////////////
void mg_egl_surface_init_for_native(mg_egl_surface* surface, void* nativeSurface)
{
surface->interface.backend = MG_BACKEND_GLES;
surface->interface.destroy = mg_egl_surface_destroy;
surface->interface.prepare = mg_egl_surface_prepare;
surface->interface.present = mg_egl_surface_present;
surface->interface.swapInterval = mg_egl_surface_swap_interval;
surface->interface.contentsScaling = mg_egl_surface_contents_scaling;
surface->interface.getFrame = mg_egl_surface_get_frame;
surface->interface.setFrame = mg_egl_surface_set_frame;
surface->interface.getHidden = mg_egl_surface_get_hidden;
surface->interface.setHidden = mg_egl_surface_set_hidden;
surface->interface.nativeLayer = mg_egl_surface_native_layer;
surface->interface.remoteId = mg_egl_surface_remote_id;
EGLAttrib displayAttribs[] = {
EGL_PLATFORM_ANGLE_TYPE_ANGLE, MG_EGL_PLATFORM_ANGLE_TYPE,
@ -174,7 +120,7 @@ void mg_egl_surface_init_for_native(mg_egl_surface* surface, void* nativeSurface
EGLint const surfaceAttributes[] = {EGL_NONE};
surface->eglSurface = eglCreateWindowSurface(surface->eglDisplay,
surface->eglConfig,
mp_layer_native_surface(&surface->layer),
nativeLayer,
surfaceAttributes);
eglBindAPI(EGL_OPENGL_ES_API);
@ -194,7 +140,7 @@ void mg_egl_surface_init_for_native(mg_egl_surface* surface, void* nativeSurface
eglSwapInterval(surface->eglDisplay, 1);
}
mg_surface_data* mg_egl_surface_create_for_sharing(u32 width, u32 height)
mg_surface_data* mg_egl_surface_create_remote(u32 width, u32 height)
{
mg_egl_surface* surface = 0;
@ -203,8 +149,8 @@ mg_surface_data* mg_egl_surface_create_for_sharing(u32 width, u32 height)
{
memset(surface, 0, sizeof(mg_egl_surface));
mp_layer_init_for_sharing(&surface->layer, width, height);
mg_egl_surface_init_for_native(surface, mp_layer_native_surface(&surface->layer));
mg_surface_init_remote((mg_surface_data*)surface, width, height);
mg_egl_surface_init(surface);
}
return((mg_surface_data*)surface);
@ -221,8 +167,8 @@ mg_surface_data* mg_egl_surface_create_for_window(mp_window window)
{
memset(surface, 0, sizeof(mg_egl_surface));
mp_layer_init_for_window(&surface->layer, windowData);
mg_egl_surface_init_for_native(surface, mp_layer_native_surface(&surface->layer));
mg_surface_init_for_window((mg_surface_data*)surface, windowData);
mg_egl_surface_init(surface);
}
}
return((mg_surface_data*)surface);

View File

@ -13,6 +13,6 @@
#include"mp_app.h"
mg_surface_data* mg_egl_surface_create_for_window(mp_window window);
mg_surface_data* mg_egl_surface_create_for_sharing(u32 width, u32 height);
mg_surface_data* mg_egl_surface_create_remote(u32 width, u32 height);
#endif // __EGL_SURFACE_H_

View File

@ -472,7 +472,7 @@ mg_surface mg_surface_create_remote(u32 width, u32 height, mg_backend_id backend
{
#if MG_COMPILE_BACKEND_GLES
case MG_BACKEND_GLES:
surface = mg_egl_surface_create_for_sharing(width, height);
surface = mg_egl_surface_create_remote(width, height);
break;
#endif
@ -486,24 +486,23 @@ mg_surface mg_surface_create_remote(u32 width, u32 height, mg_backend_id backend
return(surfaceHandle);
}
mg_surface_id mg_surface_remote_id(mg_surface handle)
mg_surface mg_surface_create_host(mp_window window)
{
mg_surface_id remoteId = 0;
mg_surface_data* surface = mg_surface_data_from_handle(handle);
if(surface && surface->remoteId)
if(__mgData.init)
{
remoteId = surface->remoteId(surface);
}
return(remoteId);
mg_init();
}
mg_surface handle = mg_surface_nil();
mg_surface_data* surface = 0;
#if OS_MACOS
surface = mg_osx_surface_create_host(window);
#endif
void mg_surface_host_connect(mg_surface handle, mg_surface_id remoteID)
if(surface)
{
mg_surface_data* surface = mg_surface_data_from_handle(handle);
if(surface && surface->connect)
{
surface->connect(surface, remoteID);
handle = mg_surface_handle_alloc(surface);
}
return(handle);
}
void mg_surface_destroy(mg_surface handle)
@ -521,7 +520,7 @@ void mg_surface_prepare(mg_surface surface)
{
DEBUG_ASSERT(__mgData.init);
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData)
if(surfaceData && surfaceData->prepare)
{
surfaceData->prepare(surfaceData);
}
@ -531,7 +530,7 @@ void mg_surface_present(mg_surface surface)
{
DEBUG_ASSERT(__mgData.init);
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData)
if(surfaceData && surfaceData->present)
{
surfaceData->present(surfaceData);
}
@ -541,7 +540,7 @@ void mg_surface_swap_interval(mg_surface surface, int swap)
{
DEBUG_ASSERT(__mgData.init);
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData)
if(surfaceData && surfaceData->swapInterval)
{
surfaceData->swapInterval(surfaceData, swap);
}
@ -552,7 +551,7 @@ vec2 mg_surface_contents_scaling(mg_surface surface)
DEBUG_ASSERT(__mgData.init);
vec2 scaling = {1, 1};
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData)
if(surfaceData && surfaceData->contentsScaling)
{
scaling = surfaceData->contentsScaling(surfaceData);
}
@ -564,7 +563,7 @@ void mg_surface_set_frame(mg_surface surface, mp_rect frame)
{
DEBUG_ASSERT(__mgData.init);
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData)
if(surfaceData && surfaceData->setFrame)
{
surfaceData->setFrame(surfaceData, frame);
}
@ -575,7 +574,7 @@ mp_rect mg_surface_get_frame(mg_surface surface)
DEBUG_ASSERT(__mgData.init);
mp_rect res = {0};
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData)
if(surfaceData && surfaceData->getFrame)
{
res = surfaceData->getFrame(surfaceData);
}
@ -586,7 +585,7 @@ void mg_surface_set_hidden(mg_surface surface, bool hidden)
{
DEBUG_ASSERT(__mgData.init);
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData)
if(surfaceData && surfaceData->setHidden)
{
surfaceData->setHidden(surfaceData, hidden);
}
@ -597,7 +596,7 @@ bool mg_surface_get_hidden(mg_surface surface)
DEBUG_ASSERT(__mgData.init);
bool res = false;
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData)
if(surfaceData && surfaceData->getHidden)
{
res = surfaceData->getHidden(surfaceData);
}
@ -608,13 +607,33 @@ void* mg_surface_native_layer(mg_surface surface)
{
void* res = 0;
mg_surface_data* surfaceData = mg_surface_data_from_handle(surface);
if(surfaceData)
if(surfaceData && surfaceData->nativeLayer)
{
res = surfaceData->nativeLayer(surfaceData);
}
return(res);
}
mg_surface_id mg_surface_remote_id(mg_surface handle)
{
mg_surface_id remoteId = 0;
mg_surface_data* surface = mg_surface_data_from_handle(handle);
if(surface && surface->remoteID)
{
remoteId = surface->remoteID(surface);
}
return(remoteId);
}
void mg_surface_host_connect(mg_surface handle, mg_surface_id remoteID)
{
mg_surface_data* surface = mg_surface_data_from_handle(handle);
if(surface && surface->hostConnect)
{
surface->hostConnect(surface, remoteID);
}
}
//------------------------------------------------------------------------------------------
//NOTE(martin): graphics canvas internal
//------------------------------------------------------------------------------------------

View File

@ -10,6 +10,7 @@
#define __GRAPHICS_INTERNAL_H_
#include"graphics.h"
#include"mp_app_internal.h"
#ifdef __cplusplus
extern "C" {
@ -31,11 +32,12 @@ typedef bool (*mg_surface_get_hidden_proc)(mg_surface_data* surface);
typedef void (*mg_surface_set_hidden_proc)(mg_surface_data* surface, bool hidden);
typedef void* (*mg_surface_native_layer_proc)(mg_surface_data* surface);
typedef mg_surface_id (*mg_surface_remote_id_proc)(mg_surface_data* surface);
typedef void (*mg_surface_connect_proc)(mg_surface_data* surface, mg_surface_id remoteId);
typedef void (*mg_surface_host_connect_proc)(mg_surface_data* surface, mg_surface_id remoteId);
typedef struct mg_surface_data
{
mg_backend_id backend;
mp_layer layer;
mg_surface_destroy_proc destroy;
mg_surface_prepare_proc prepare;
@ -47,12 +49,17 @@ typedef struct mg_surface_data
mg_surface_get_hidden_proc getHidden;
mg_surface_set_hidden_proc setHidden;
mg_surface_native_layer_proc nativeLayer;
mg_surface_remote_id_proc remoteId;
mg_surface_connect_proc connect;
mg_surface_remote_id_proc remoteID;
mg_surface_host_connect_proc hostConnect;
} mg_surface_data;
mg_surface mg_surface_alloc_handle(mg_surface_data* surface);
mg_surface_data* mg_surface_data_from_handle(mg_surface handle);
void mg_surface_init_for_window(mg_surface_data* surface, mp_window_data* window);
void mg_surface_init_remote(mg_surface_data* surface, u32 width, u32 height);
void mg_surface_init_host(mg_surface_data* surface, mp_window_data* window);
void mg_surface_cleanup(mg_surface_data* surface);
void* mg_surface_native_layer(mg_surface surface);
//---------------------------------------------------------------

View File

@ -20,8 +20,6 @@
#include"egl_surface.c"
#endif
#include"osx_surface_sharing.m"
/*
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

View File

@ -150,21 +150,4 @@ typedef struct mp_app
MP_PLATFORM_APP_DATA
} mp_app;
//--------------------------------------------------------------------
// layer management
//--------------------------------------------------------------------
typedef struct mp_layer mp_layer;
void mp_layer_init_for_window(mp_layer* layer, mp_window_data* window);
void mp_layer_init_for_sharing(mp_layer* layer, u32 width, u32 height);
void mp_layer_cleanup(mp_layer* layer);
vec2 mp_layer_contents_scaling(mp_layer* layer);
mp_rect mp_layer_get_frame(mp_layer* layer);
void mp_layer_set_frame(mp_layer* layer, mp_rect frame);
void mp_layer_set_hidden(mp_layer* layer, bool hidden);
bool mp_layer_get_hidden(mp_layer* layer);
void* mp_layer_native_surface(mp_layer* layer);
#endif // __MP_APP_INTERNAL_H_

View File

@ -23,8 +23,6 @@ typedef struct mg_mtl_surface
{
mg_surface_data interface;
mp_layer layer;
// permanent mtl resources
id<MTLDevice> device;
CAMetalLayer* mtlLayer;
@ -128,45 +126,16 @@ void mg_mtl_surface_swap_interval(mg_surface_data* interface, int swap)
////////////////////////////////////////////////////////////////
}
vec2 mg_mtl_surface_contents_scaling(mg_surface_data* interface)
{
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
return(mp_layer_contents_scaling(&surface->layer));
}
void mg_mtl_surface_set_frame(mg_surface_data* interface, mp_rect frame)
{
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
mp_layer_set_frame(&surface->layer, frame);
vec2 scale = mp_layer_contents_scaling(&surface->layer);
mg_osx_surface_set_frame(interface, frame);
vec2 scale = mg_osx_surface_contents_scaling(interface);
CGSize drawableSize = (CGSize){.width = frame.w * scale.x, .height = frame.h * scale.y};
surface->mtlLayer.drawableSize = drawableSize;
}
mp_rect mg_mtl_surface_get_frame(mg_surface_data* interface)
{
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
return(mp_layer_get_frame(&surface->layer));
}
void mg_mtl_surface_set_hidden(mg_surface_data* interface, bool hidden)
{
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
mp_layer_set_hidden(&surface->layer, hidden);
}
bool mg_mtl_surface_get_hidden(mg_surface_data* interface)
{
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
return(mp_layer_get_hidden(&surface->layer));
}
void* mg_mtl_surface_native_layer(mg_surface_data* interface)
{
mg_mtl_surface* surface = (mg_mtl_surface*)interface;
return((void*)surface->mtlLayer);
}
//TODO fix that according to real scaling, depending on the monitor settings
static const f32 MG_MTL_SURFACE_CONTENTS_SCALING = 2;
@ -179,18 +148,16 @@ mg_surface_data* mg_mtl_surface_create_for_window(mp_window window)
{
surface = (mg_mtl_surface*)malloc(sizeof(mg_mtl_surface));
mg_surface_init_for_window((mg_surface_data*)surface, windowData);
//NOTE(martin): setup interface functions
surface->interface.backend = MG_BACKEND_METAL;
surface->interface.destroy = mg_mtl_surface_destroy;
surface->interface.prepare = mg_mtl_surface_prepare;
surface->interface.present = mg_mtl_surface_present;
surface->interface.swapInterval = mg_mtl_surface_swap_interval;
surface->interface.contentsScaling = mg_mtl_surface_contents_scaling;
surface->interface.getFrame = mg_mtl_surface_get_frame;
surface->interface.setFrame = mg_mtl_surface_set_frame;
surface->interface.getHidden = mg_mtl_surface_get_hidden;
surface->interface.setHidden = mg_mtl_surface_set_hidden;
surface->interface.nativeLayer = mg_mtl_surface_native_layer;
@autoreleasepool
{
@ -208,7 +175,7 @@ mg_surface_data* mg_mtl_surface_create_for_window(mp_window window)
surface->mtlLayer.device = surface->device;
[surface->mtlLayer setOpaque:NO];
surface->layer.caLayer = (CALayer*)surface->mtlLayer;
[surface->interface.layer.caLayer addSublayer: (CALayer*)surface->mtlLayer];
//-----------------------------------------------------------
//NOTE(martin): set the size and scaling
@ -223,8 +190,6 @@ mg_surface_data* mg_mtl_surface_create_for_window(mp_window window)
surface->mtlLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
[windowData->osx.nsView.layer addSublayer: surface->mtlLayer];
//NOTE(martin): handling resizing
surface->mtlLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
surface->mtlLayer.needsDisplayOnBoundsChange = YES;

View File

@ -21,6 +21,7 @@
#define NSTimer void
#define NSCursor void
#define CALayer void
#define CAContext void
#endif
#include<Carbon/Carbon.h>
@ -33,11 +34,6 @@ typedef struct osx_window_data
} osx_window_data;
typedef struct mp_layer
{
CALayer* caLayer;
} mp_layer;
#define MP_PLATFORM_WINDOW_DATA osx_window_data osx;
const u32 MP_APP_MAX_VIEWS = 128;
@ -55,6 +51,37 @@ typedef struct osx_app_data
#define MP_PLATFORM_APP_DATA osx_app_data osx;
//-----------------------------------------------
// Surface layer
//-----------------------------------------------
#ifdef __OBJC__
//NOTE: these private interfaces for surface sharing need to be declared explicitly here
typedef uint32_t CGSConnectionID;
CGSConnectionID CGSMainConnectionID(void);
typedef uint32_t CAContextID;
@interface CAContext : NSObject
{
}
+ (id)contextWithCGSConnection:(CAContextID)contextId options:(NSDictionary*)optionsDict;
@property(readonly) CAContextID contextId;
@property(retain) CALayer *layer;
@end
@interface CALayerHost : CALayer
{
}
@property CAContextID contextId;
@end
#endif
typedef struct mp_layer
{
CALayer* caLayer;
CAContext* caContext;
} mp_layer;
#endif //__OSX_APP_H_

View File

@ -17,6 +17,7 @@
#include"memory.h"
#include"macro_helpers.h"
#include"platform_clock.h"
#include"graphics_internal.h"
#include"mp_app.c"
@ -1716,41 +1717,24 @@ void mp_window_set_content_size(mp_window window, int width, int height)
}
//--------------------------------------------------------------------
// layers
// platform surface
//--------------------------------------------------------------------
void mp_layer_init_for_window(mp_layer* layer, mp_window_data* window)
{@autoreleasepool{
layer->caLayer = [[CALayer alloc] init];
[layer->caLayer retain];
NSRect frame = [[window->osx.nsWindow contentView] frame];
CGSize size = frame.size;
layer->caLayer.frame = (CGRect){{0, 0}, size};
[window->osx.nsView.layer addSublayer: layer->caLayer];
}}
void mp_layer_cleanup(mp_layer* layer)
{@autoreleasepool{
[layer->caLayer release];
}}
void* mp_layer_native_surface(mp_layer* layer)
void* mg_osx_surface_native_layer(mg_surface_data* surface)
{
return((void*)layer->caLayer);
return((void*)surface->layer.caLayer);
}
vec2 mp_layer_contents_scaling(mp_layer* layer)
vec2 mg_osx_surface_contents_scaling(mg_surface_data* surface)
{@autoreleasepool{
f32 contentsScale = [layer->caLayer contentsScale];
f32 contentsScale = [surface->layer.caLayer contentsScale];
vec2 res = {contentsScale, contentsScale};
return(res);
}}
mp_rect mp_layer_get_frame(mp_layer* layer)
mp_rect mg_osx_surface_get_frame(mg_surface_data* surface)
{@autoreleasepool{
CGRect frame = layer->caLayer.frame;
CGRect frame = surface->layer.caLayer.frame;
mp_rect res = {frame.origin.x,
frame.origin.y,
frame.size.width,
@ -1758,25 +1742,129 @@ mp_rect mp_layer_get_frame(mp_layer* layer)
return(res);
}}
void mp_layer_set_frame(mp_layer* layer, mp_rect frame)
void mg_osx_surface_set_frame(mg_surface_data* surface, mp_rect frame)
{@autoreleasepool{
CGRect cgFrame = {{frame.x, frame.y}, {frame.w, frame.h}};
[layer->caLayer setFrame: cgFrame];
[surface->layer.caLayer setFrame: cgFrame];
}}
void mp_layer_set_hidden(mp_layer* layer, bool hidden)
bool mg_osx_surface_get_hidden(mg_surface_data* surface)
{@autoreleasepool{
return([surface->layer.caLayer isHidden]);
}}
void mg_osx_surface_set_hidden(mg_surface_data* surface, bool hidden)
{@autoreleasepool{
[CATransaction begin];
[CATransaction setDisableActions:YES];
[layer->caLayer setHidden:hidden];
[surface->layer.caLayer setHidden:hidden];
[CATransaction commit];
}}
bool mp_layer_get_hidden(mp_layer* layer)
void mg_surface_cleanup(mg_surface_data* surface)
{@autoreleasepool{
return([layer->caLayer isHidden]);
[surface->layer.caLayer release];
}}
void mg_surface_init_for_window(mg_surface_data* surface, mp_window_data* window)
{@autoreleasepool{
surface->nativeLayer = mg_osx_surface_native_layer;
surface->contentsScaling = mg_osx_surface_contents_scaling;
surface->getFrame = mg_osx_surface_get_frame;
surface->setFrame = mg_osx_surface_set_frame;
surface->getHidden = mg_osx_surface_get_hidden;
surface->setHidden = mg_osx_surface_set_hidden;
surface->layer.caLayer = [[CALayer alloc] init];
[surface->layer.caLayer retain];
NSRect frame = [[window->osx.nsWindow contentView] frame];
CGSize size = frame.size;
surface->layer.caLayer.frame = (CGRect){{0, 0}, size};
[window->osx.nsView.layer addSublayer: surface->layer.caLayer];
}}
//------------------------------------------------------------------------------------------------
// Remote surfaces
//------------------------------------------------------------------------------------------------
mg_surface_id mg_osx_surface_remote_id(mg_surface_data* surface)
{
mg_surface_id remoteID = 0;
if(surface->layer.caContext)
{
@autoreleasepool
{
remoteID = (mg_surface_id)[surface->layer.caContext contextId];
}
}
return(remoteID);
}
void mg_surface_init_remote(mg_surface_data* surface, u32 width, u32 height)
{@autoreleasepool{
surface->nativeLayer = mg_osx_surface_native_layer;
surface->contentsScaling = mg_osx_surface_contents_scaling;
surface->getFrame = mg_osx_surface_get_frame;
surface->setFrame = mg_osx_surface_set_frame;
surface->getHidden = mg_osx_surface_get_hidden;
surface->setHidden = mg_osx_surface_set_hidden;
surface->remoteID = mg_osx_surface_remote_id;
surface->layer.caLayer = [[CALayer alloc] init];
[surface->layer.caLayer retain];
[surface->layer.caLayer setFrame: (CGRect){{0, 0}, {width, height}}];
NSDictionary* dict = [[NSDictionary alloc] init];
CGSConnectionID connectionID = CGSMainConnectionID();
surface->layer.caContext = [CAContext contextWithCGSConnection: connectionID options: dict];
[surface->layer.caContext retain];
[surface->layer.caContext setLayer:surface->layer.caLayer];
}}
void mg_osx_surface_host_connect(mg_surface_data* surface, mg_surface_id remoteID)
{@autoreleasepool{
[(CALayerHost*)surface->layer.caLayer setContextId: (CAContextID)remoteID];
}}
void mg_surface_init_host(mg_surface_data* surface, mp_window_data* window)
{@autoreleasepool{
surface->nativeLayer = mg_osx_surface_native_layer;
surface->contentsScaling = mg_osx_surface_contents_scaling;
surface->getFrame = mg_osx_surface_get_frame;
surface->setFrame = mg_osx_surface_set_frame;
surface->getHidden = mg_osx_surface_get_hidden;
surface->setHidden = mg_osx_surface_set_hidden;
surface->hostConnect = mg_osx_surface_host_connect;
surface->layer.caLayer = [[CALayerHost alloc] init];
[surface->layer.caLayer retain];
NSRect frame = [[window->osx.nsWindow contentView] frame];
CGSize size = frame.size;
[surface->layer.caLayer setFrame: (CGRect){{0, 0}, size}];
[window->osx.nsView.layer addSublayer: surface->layer.caLayer];
}}
mg_surface_data* mg_osx_surface_create_host(mp_window windowHandle)
{
mg_surface_data* surface = 0;
mp_window_data* window = mp_window_ptr_from_handle(windowHandle);
if(window)
{
surface = malloc_type(mg_surface_data);
if(surface)
{
mg_surface_init_host(surface, window);
}
}
return(surface);
}
//--------------------------------------------------------------------
// view management
//--------------------------------------------------------------------

View File

@ -1,250 +0,0 @@
/************************************************************//**
*
* @file: osx_surface_sharing.m
* @author: Martin Fouilleul
* @date: 01/03/2023
* @revision:
*
*****************************************************************/
#include"graphics_internal.h"
//------------------------------------------------------------------------------------------------
//NOTE: these private interfaces need to be declared explicitly...
//------------------------------------------------------------------------------------------------
typedef uint32_t CGSConnectionID;
CGSConnectionID CGSMainConnectionID(void);
typedef uint32_t CAContextID;
@interface CAContext : NSObject
{
}
+ (id)contextWithCGSConnection:(CAContextID)contextId options:(NSDictionary*)optionsDict;
@property(readonly) CAContextID contextId;
@property(retain) CALayer *layer;
@end
@interface CALayerHost : CALayer
{
}
@property CAContextID contextId;
@end
//------------------------------------------------------------------------------------------------
// Surface server
//------------------------------------------------------------------------------------------------
typedef struct mg_surface_server_data
{
CAContext* context;
} mg_surface_server_data;
mg_surface_server_data* mg_surface_server_data_from_handle(mg_surface_server handle)
{
mg_surface_server_data* server = (mg_surface_server_data*)mg_data_from_handle(MG_HANDLE_SURFACE_SERVER, handle.h);
return(server);
}
MP_API mg_surface_server mg_surface_server_create(void)
{
mg_surface_server_data* server = malloc_type(mg_surface_server_data);
@autoreleasepool
{
NSDictionary* dict = [[NSDictionary alloc] init];
CGSConnectionID connectionID = CGSMainConnectionID();
server->context = [CAContext contextWithCGSConnection: connectionID options: dict];
[server->context retain];
}
mg_surface_server handle = (mg_surface_server){mg_handle_alloc(MG_HANDLE_SURFACE_SERVER, (void*)server)};
return(handle);
}
MP_API void mg_surface_server_destroy(mg_surface_server handle)
{
mg_surface_server_data* server = mg_surface_server_data_from_handle(handle);
if(server)
{
@autoreleasepool
{
[server->context release];
}
free(server);
mg_handle_recycle(handle.h);
}
}
MP_API mg_surface_connection_id mg_surface_server_start(mg_surface_server handle, mg_surface surface)
{
mg_surface_connection_id res = 0;
mg_surface_server_data* server = mg_surface_server_data_from_handle(handle);
if(server)
{
@autoreleasepool
{
CALayer* layer = mg_surface_native_layer(surface);
[server->context setLayer: layer];
CAContextID contextID = [server->context contextId];
res = (mg_surface_connection_id)contextID;
}
}
return(res);
}
MP_API void mg_surface_server_stop(mg_surface_server handle)
{
mg_surface_server_data* server = mg_surface_server_data_from_handle(handle);
if(server)
{
@autoreleasepool
{
[server->context setLayer: nil];
}
}
}
//------------------------------------------------------------------------------------------------
// Surface client
//------------------------------------------------------------------------------------------------
typedef struct mg_osx_surface_client
{
mg_surface_data interface;
mp_layer layer;
CALayerHost* layerHost;
} mg_osx_surface_client;
void mg_osx_surface_client_prepare(mg_surface_data* interface)
{}
void mg_osx_surface_client_present(mg_surface_data* interface)
{}
void mg_osx_surface_client_swap_interval(mg_surface_data* interface, int swap)
{
//TODO
}
vec2 mg_osx_surface_client_contents_scaling(mg_surface_data* interface)
{
mg_osx_surface_client* surface = (mg_osx_surface_client*)interface;
return(mp_layer_contents_scaling(&surface->layer));
}
mp_rect mg_osx_surface_client_get_frame(mg_surface_data* interface)
{
mg_osx_surface_client* surface = (mg_osx_surface_client*)interface;
return(mp_layer_get_frame(&surface->layer));
}
void mg_osx_surface_client_set_frame(mg_surface_data* interface, mp_rect frame)
{
mg_osx_surface_client* surface = (mg_osx_surface_client*)interface;
mp_layer_set_frame(&surface->layer, frame);
}
void mg_osx_surface_client_set_hidden(mg_surface_data* interface, bool hidden)
{
mg_osx_surface_client* surface = (mg_osx_surface_client*)interface;
mp_layer_set_hidden(&surface->layer, hidden);
}
bool mg_osx_surface_client_get_hidden(mg_surface_data* interface)
{
mg_osx_surface_client* surface = (mg_osx_surface_client*)interface;
return(mp_layer_get_hidden(&surface->layer));
}
void* mg_osx_surface_client_native_layer(mg_surface_data* interface)
{
mg_osx_surface_client* surface = (mg_osx_surface_client*)interface;
return(mp_layer_native_surface(&surface->layer));
}
void mg_osx_surface_client_destroy(mg_surface_data* interface)
{
mg_osx_surface_client* surface = (mg_osx_surface_client*)interface;
@autoreleasepool
{
[surface->layerHost release];
}
free(surface);
}
mg_surface_data* mg_osx_surface_client_create_for_window(mp_window window)
{
mg_osx_surface_client* surface = 0;
mp_window_data* windowData = mp_window_ptr_from_handle(window);
if(windowData)
{
surface = malloc_type(mg_osx_surface_client);
if(surface)
{
mp_layer_init_for_window(&surface->layer, windowData);
surface->interface.backend = MG_BACKEND_REMOTE;
surface->interface.destroy = mg_osx_surface_client_destroy;
surface->interface.prepare = mg_osx_surface_client_prepare;
surface->interface.present = mg_osx_surface_client_present;
surface->interface.swapInterval = mg_osx_surface_client_swap_interval;
surface->interface.contentsScaling = mg_osx_surface_client_contents_scaling;
surface->interface.getFrame = mg_osx_surface_client_get_frame;
surface->interface.setFrame = mg_osx_surface_client_set_frame;
surface->interface.getHidden = mg_osx_surface_client_get_hidden;
surface->interface.setHidden = mg_osx_surface_client_set_hidden;
surface->interface.nativeLayer = mg_osx_surface_client_native_layer;
@autoreleasepool
{
surface->layerHost = [[CALayerHost alloc] init];
[surface->layerHost retain];
CALayer* caLayer = mp_layer_native_surface(&surface->layer);
[caLayer addSublayer:surface->layerHost];
}
}
}
return((mg_surface_data*)surface);
}
mg_surface mg_surface_client_create_for_window(mp_window window)
{
if(!__mgData.init)
{
mg_init();
}
mg_surface surfaceHandle = {0};
mg_surface_data* surface = mg_osx_surface_client_create_for_window(window);
if(surface)
{
surfaceHandle = mg_surface_handle_alloc(surface);
}
return(surfaceHandle);
}
MP_API void mg_surface_client_connect(mg_surface handle, mg_surface_connection_id ID)
{
mg_surface_data* interface = mg_surface_data_from_handle(handle);
if(interface && interface->backend == MG_BACKEND_REMOTE)
{
mg_osx_surface_client* surface = (mg_osx_surface_client*)interface;
CAContextID contextID = (CAContextID)((uintptr_t)ID);
[surface->layerHost setContextId: contextID];
}
}
MP_API void mg_surface_client_disconnect(mg_surface handle)
{
mg_surface_data* interface = mg_surface_data_from_handle(handle);
if(interface && interface->backend == MG_BACKEND_REMOTE)
{
mg_osx_surface_client* surface = (mg_osx_surface_client*)interface;
[surface->layerHost setContextId: 0];
}
}

View File

@ -1,113 +0,0 @@
Various notes/thoughts about the 2D vector graphics renderer
Triangle Rasterization
----------------------
https://fgiesen.wordpress.com/2013/02/08/triangle-rasterization-in-practice/
https://github.com/rygorous/trirast/blob/master/main.cpp
https://joshbeam.com/articles/triangle_rasterization/
https://nlguillemot.wordpress.com/2016/07/10/rasterizer-notes/
https://web.archive.org/web/20120625103536/http://devmaster.net/forums/topic/1145-advanced-rasterization/
Bindless textures
-----------------
It's tempting to use bindless textures to be able to draw individual images using only one draw call. This would avoid much of the complexity of either managing a texture atlas under the hood or breaking the draw list into batches...
But, it's only an extension and seem to not be supported everywhere. Moreover, there might be a problem where the texture handle used by the shader can not differ between batches (must be "dynamic uniforms"), which defeats the purpose in our case -> it requires OES_gpu_shader5 or GLES 3.2
ideally, the atlas should be built on top of lower level image features of the renderer, eg mg_image_upload_sub_region(), mg_image_render_sub_region() etc...
This would mean individual textures can be set and used in a frame. So without bindless textures, we would need to break down the draw list in batches, each time the texture attribute changes. This also mean we need to blend each batch result to the previous one.
- It seems possible to implement bindless texture in metal using argument buffers
- We could investigate if angle/our targets likely support OES_gpu_shader5
- But, this means the canvas renderer relies on the backend to provide this kind of feature
- It also assume the upper bound for indexable bindless textures is enough on every backend
- We'll likely need a batching fallback anyway?
-> Angle doesn't seem to support GL_IMG_bindless_textures for now.
Workaround: we could use a desktop GL 4.3+ context for the canvas renderer on windows, _BUT_ the functions would conflict with the GLES canvas. Except if we use function pointers that are loaded differently for each context (which we probably should but I'd better keep it for later).
-> We'll probably want to do that, or make 1 draw call per changing texture.
Anyway for now, is it possible have an _under the hood_ atlas, and reserve a way to change the API so that we make the atlas explicit / allow using single textures for big images etc?
* We could decide that we can set an atlas, and all mg_images get allocated from that atlas. If no atlas is set a default one is used.
* or, we can expose mg_texture and related APIs for now, as if they were individual, but back them by a hidden atlas. And a bit later expose mg_image/atlas -> maybe better.
* Or just implement breaking the triangle stream into batches now...
Perf issue of binding large vertex buffer
-----------------------------------------
Binding big buffers has a high cost. We should send updates in smaller batches, either
- use a temporary storage to build vertex buffer, then send with glBufferData just before rendering
- stream data to large buffer using glMapBufferRange (instead of mapping the whole buffer)
- stream data to large buffer using glBufferSubData
We have to account for these edge cases:
- how we handle overflowing the sub range (ie space allocated or mapped to build vertices)
- how we handle overflowing buffer capacity (if using a pre-allocated buffer and glMapBufferRange/glBufferSubData)
* Using a temporary store and glBufferData forces a draw call when exceeding the temporary buffer limits. But the two cases of overflow are handled at once.
* Using a temporary store and glBufferSubData distributed data transfer asynchronously, and doesn't force a draw call when exceeding temporary buffer. We'd still need to force a draw when exceeding the gl buffer size.
* Using glMapRange also distributes data transfer asynchronously. We need to force a draw when exceeding the gl buffer size.
The first solution (temporary building buffer + glBufferData) is simpler and probably ok for low number of vertices. We can even build the vertices in an arena and virtually never care about exceeding building buffer capacity. But if we have many vertices, maybe we care about distributing transfer across asynchronous calls.
what happens if we exceed the gl buffer size? -> we need to make a draw call to use vertices, and maybe then grow the buffer to bigger size. But this implies breaking the batch, probably in the middle of a shape? this isn't really possible because we'd need the previous candidate color and flipcount transfered between batches. We could use a texture for that, but it complicates things quite a bit...
Notes:
* Mapping/Unmapping smaller ranges of a big buffer doesn't seem to lower the cost of binding that buffer. Does the driver sends the full buffer regardless of the range that was changed?
* Orphaning the buffer before mapping doesn't seem to do any good
* Doing glBufferData from a small build buffer is surprisingly slow...
* Angle seems to take into account only the first call to glBufferData to allocate size, and then send the full buffer???
* Not pre-allocating in creation procedure "solves" the problem with glBufferData...
Only way seems to be to reduce buffer size:
* pack vec2 together to avoid padding
* store color, clip and uv out of band?
* eg have a shape buffer containing uv for bounding box, clip, and color?
* could also still store color as an int "for free" next to shape index
* could send pos and uv as half-floats?
Ideally, we would do the vertex computation on the GPU (opportunity to parallelize AND avoid sending vertex buffer...)
-> Problem profiling canvas renderer with angle (not really well supported in renderdoc)
-> Maybe use an OpenGL backend instead?
Quick measurement on perf_text.exe
----------------------------------
* Re-allocate and copy each time with glBufferData --> ~23ms
* Allocate big buffer and update with glBufferSubData --> ~23ms
* Map whole buffer --> ~44ms
* Map whole buffer with GL_MAP_INVALIDATE_BUFFER_BIT --> ~19ms (but with some startup hiccups...)
* Map whole buffer with GL_MAP_INVALIDATE_RANGE_BIT --> ~44ms
-> Stutter is with GL_MAP_INVALIDATE_BUFFER_BIT isn't reassuring. Stick to glBufferData for now.
-> May be worth it to try persistently mapped buffers later.
* Splitting vertex data and shape data (using glBufferData) --> ~10ms
Backend Selection
-----------------
* We need define macros to select which backends are compiled into milepost.lib
-> define default backends and allow override with user compile defines
* We also need define macros to select which backend-specific functions and/or graphics APIs to include when building an app with milepost (e.g.\ include opengl headers or not, etc.).
* For now, we intend to statically link milepost.lib (or dynamically load to an embedded dll), so at application compile time the supported backends are known and calling a backend-specific function for a backend that has not been compiled in milepost results in a link error. (No need to defines for that)
* However, if we want to provide a nice abstraction for creation function, this means it is possible for user to pass a backend that does not exists in the lib (resulting in a runtime error). It would be nice to have some way to runtime check which backends are available. That can be a function compiled in the lib.
* We also might want to access backend-specific functions in the app, so we need to include those conditionally.
* We also might want to include graphics API, eg OpenGL, in a uniform way. -> See later how we manage GLES/GL co-existence