[surface] abstracted child window layer from win32 surfaces

This commit is contained in:
martinfouilleul 2023-02-21 12:35:22 +01:00
parent 5ac512c15e
commit 9ea9ea7636
7 changed files with 148 additions and 195 deletions

View File

@ -19,8 +19,7 @@ typedef struct mg_egl_surface
{ {
mg_surface_data interface; mg_surface_data interface;
HWND hWnd; mp_layer layer;
vec2 contentsScaling;
EGLDisplay eglDisplay; EGLDisplay eglDisplay;
EGLConfig eglConfig; EGLConfig eglConfig;
@ -46,7 +45,7 @@ void mg_egl_surface_destroy(mg_surface_data* interface)
eglDestroyContext(surface->eglDisplay, surface->eglContext); eglDestroyContext(surface->eglDisplay, surface->eglContext);
eglDestroySurface(surface->eglDisplay, surface->eglSurface); eglDestroySurface(surface->eglDisplay, surface->eglSurface);
DestroyWindow(surface->hWnd); mp_layer_cleanup(&surface->layer);
free(surface); free(surface);
} }
@ -72,75 +71,33 @@ void mg_egl_surface_swap_interval(mg_surface_data* interface, int swap)
vec2 mg_egl_surface_contents_scaling(mg_surface_data* interface) vec2 mg_egl_surface_contents_scaling(mg_surface_data* interface)
{ {
mg_egl_surface* surface = (mg_egl_surface*)interface; mg_egl_surface* surface = (mg_egl_surface*)interface;
return(surface->contentsScaling); return(mp_layer_contents_scaling(&surface->layer));
} }
mp_rect mg_egl_surface_get_frame(mg_surface_data* interface) mp_rect mg_egl_surface_get_frame(mg_surface_data* interface)
{ {
mp_rect res = {0};
mg_egl_surface* surface = (mg_egl_surface*)interface; mg_egl_surface* surface = (mg_egl_surface*)interface;
if(surface) return(mp_layer_get_frame(&surface->layer));
{
RECT rect = {0};
GetClientRect(surface->hWnd, &rect);
vec2 scale = surface->contentsScaling;
res = (mp_rect){rect.left/scale.x,
rect.bottom/scale.y,
(rect.right - rect.left)/scale.x,
(rect.bottom - rect.top)/scale.y};
}
return(res);
} }
void mg_egl_surface_set_frame(mg_surface_data* interface, mp_rect frame) void mg_egl_surface_set_frame(mg_surface_data* interface, mp_rect frame)
{ {
mg_egl_surface* surface = (mg_egl_surface*)interface; mg_egl_surface* surface = (mg_egl_surface*)interface;
if(surface) mp_layer_set_frame(&surface->layer, frame);
{
HWND parent = GetParent(surface->hWnd);
RECT parentContentRect;
GetClientRect(parent, &parentContentRect);
int parentHeight = parentContentRect.bottom - parentContentRect.top;
SetWindowPos(surface->hWnd,
HWND_TOP,
frame.x * surface->contentsScaling.x,
parentHeight - (frame.y + frame.h) * surface->contentsScaling.y,
frame.w * surface->contentsScaling.x,
frame.h * surface->contentsScaling.y,
SWP_NOACTIVATE | SWP_NOZORDER);
}
} }
void mg_egl_surface_set_hidden(mg_surface_data* interface, bool hidden) void mg_egl_surface_set_hidden(mg_surface_data* interface, bool hidden)
{ {
mg_egl_surface* surface = (mg_egl_surface*)interface; mg_egl_surface* surface = (mg_egl_surface*)interface;
if(surface) mp_layer_set_hidden(&surface->layer, hidden);
{
ShowWindow(surface->hWnd, hidden ? SW_HIDE : SW_NORMAL);
}
} }
bool mg_egl_surface_get_hidden(mg_surface_data* interface) bool mg_egl_surface_get_hidden(mg_surface_data* interface)
{ {
bool hidden = false;
mg_egl_surface* surface = (mg_egl_surface*)interface; mg_egl_surface* surface = (mg_egl_surface*)interface;
if(surface) return(mp_layer_get_hidden(&surface->layer));
{
hidden = !IsWindowVisible(surface->hWnd);
}
return(hidden);
} }
LRESULT mg_egl_surface_window_proc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam)
{
return(DefWindowProc(windowHandle, message, wParam, lParam));
}
mg_surface mg_egl_surface_create_for_window(mp_window window) mg_surface mg_egl_surface_create_for_window(mp_window window)
{ {
mg_surface res = mg_surface_nil(); mg_surface res = mg_surface_nil();
@ -148,31 +105,6 @@ mg_surface mg_egl_surface_create_for_window(mp_window window)
mp_window_data* windowData = mp_window_ptr_from_handle(window); mp_window_data* windowData = mp_window_ptr_from_handle(window);
if(windowData) if(windowData)
{ {
//NOTE(martin): create a child window for the surface
WNDCLASS childWindowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
.lpfnWndProc = mg_egl_surface_window_proc,
.hInstance = GetModuleHandleW(NULL),
.lpszClassName = "egl_surface_window_class",
.hCursor = LoadCursor(0, IDC_ARROW)};
if(!RegisterClass(&childWindowClass))
{
//TODO: error
}
mp_rect frame = mp_window_get_content_rect(window);
u32 dpi = GetDpiForWindow(windowData->win32.hWnd);
vec2 contentsScaling = (vec2){(float)dpi/96., (float)dpi/96.};
HWND childWindow = CreateWindow("egl_surface_window_class", "Test",
WS_CHILD|WS_VISIBLE,
0, 0, frame.w*contentsScaling.x, frame.h*contentsScaling.y,
windowData->win32.hWnd,
0,
childWindowClass.hInstance,
0);
mg_egl_surface* surface = malloc_type(mg_egl_surface); mg_egl_surface* surface = malloc_type(mg_egl_surface);
memset(surface, 0, sizeof(mg_egl_surface)); memset(surface, 0, sizeof(mg_egl_surface));
@ -187,8 +119,7 @@ mg_surface mg_egl_surface_create_for_window(mp_window window)
surface->interface.getHidden = mg_egl_surface_get_hidden; surface->interface.getHidden = mg_egl_surface_get_hidden;
surface->interface.setHidden = mg_egl_surface_set_hidden; surface->interface.setHidden = mg_egl_surface_set_hidden;
surface->hWnd = childWindow; mp_layer_init_for_window(&surface->layer, windowData);
surface->contentsScaling = contentsScaling;
EGLAttrib displayAttribs[] = { EGLAttrib displayAttribs[] = {
EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE, EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE,
@ -217,7 +148,7 @@ mg_surface mg_egl_surface_create_for_window(mp_window window)
EGLint const surfaceAttributes[] = {EGL_NONE}; EGLint const surfaceAttributes[] = {EGL_NONE};
surface->eglSurface = eglCreateWindowSurface(surface->eglDisplay, surface->eglSurface = eglCreateWindowSurface(surface->eglDisplay,
surface->eglConfig, surface->eglConfig,
surface->hWnd, mp_layer_native_surface(&surface->layer),
surfaceAttributes); surfaceAttributes);
eglBindAPI(EGL_OPENGL_ES_API); eglBindAPI(EGL_OPENGL_ES_API);

View File

@ -2,7 +2,7 @@
* *
* file: glsl_shaders.h * file: glsl_shaders.h
* note: string literals auto-generated by embed_text.py * note: string literals auto-generated by embed_text.py
* date: 20/022023 * date: 21/022023
* *
**********************************************************************/ **********************************************************************/
#ifndef __GLSL_SHADERS_H__ #ifndef __GLSL_SHADERS_H__

View File

@ -65,7 +65,7 @@
#endif #endif
#if MG_COMPILE_BACKEND_GLES #if MG_COMPILE_BACKEND_GLES
#include"win32_egl_surface.c" #include"egl_surface.c"
#endif #endif
#elif defined(OS_MACOS) #elif defined(OS_MACOS)

View File

@ -115,10 +115,9 @@ typedef struct mg_wgl_surface
{ {
mg_surface_data interface; mg_surface_data interface;
HWND hWnd; mp_layer layer;
HDC hDC; HDC hDC;
HGLRC glContext; HGLRC glContext;
vec2 contentsScaling;
//NOTE: this may be a bit wasteful to have one api struct per surface, but win32 docs says that loading procs //NOTE: this may be a bit wasteful to have one api struct per surface, but win32 docs says that loading procs
// from different contexts might select different implementations (eg. depending on context version/pixel format) // from different contexts might select different implementations (eg. depending on context version/pixel format)
@ -135,7 +134,7 @@ void mg_wgl_surface_destroy(mg_surface_data* interface)
} }
wglDeleteContext(surface->glContext); wglDeleteContext(surface->glContext);
DestroyWindow(surface->hWnd); mp_layer_cleanup(&surface->layer);
free(surface); free(surface);
} }
@ -163,69 +162,33 @@ void mg_wgl_surface_swap_interval(mg_surface_data* interface, int swap)
vec2 mg_wgl_surface_contents_scaling(mg_surface_data* interface) vec2 mg_wgl_surface_contents_scaling(mg_surface_data* interface)
{ {
mg_wgl_surface* surface = (mg_wgl_surface*)interface; mg_wgl_surface* surface = (mg_wgl_surface*)interface;
return(surface->contentsScaling); return(mp_layer_contents_scaling(&surface->layer));
} }
mp_rect mg_wgl_surface_get_frame(mg_surface_data* interface) mp_rect mg_wgl_surface_get_frame(mg_surface_data* interface)
{ {
mp_rect res = {0};
mg_wgl_surface* surface = (mg_wgl_surface*)interface; mg_wgl_surface* surface = (mg_wgl_surface*)interface;
if(surface) return(mp_layer_get_frame(&surface->layer));
{
RECT rect = {0};
GetClientRect(surface->hWnd, &rect);
vec2 scale = surface->contentsScaling;
res = (mp_rect){rect.left/scale.x,
rect.bottom/scale.y,
(rect.right - rect.left)/scale.x,
(rect.bottom - rect.top)/scale.y};
}
return(res);
} }
void mg_wgl_surface_set_frame(mg_surface_data* interface, mp_rect frame) void mg_wgl_surface_set_frame(mg_surface_data* interface, mp_rect frame)
{ {
mg_wgl_surface* surface = (mg_wgl_surface*)interface; mg_wgl_surface* surface = (mg_wgl_surface*)interface;
if(surface) mp_layer_set_frame(&surface->layer, frame);
{
HWND parent = GetParent(surface->hWnd);
RECT parentContentRect;
GetClientRect(parent, &parentContentRect);
int parentHeight = parentContentRect.bottom - parentContentRect.top;
SetWindowPos(surface->hWnd,
HWND_TOP,
frame.x * surface->contentsScaling.x,
parentHeight - (frame.y + frame.h) * surface->contentsScaling.y,
frame.w * surface->contentsScaling.x,
frame.h * surface->contentsScaling.y,
SWP_NOACTIVATE | SWP_NOZORDER);
}
} }
void mg_wgl_surface_set_hidden(mg_surface_data* interface, bool hidden) void mg_wgl_surface_set_hidden(mg_surface_data* interface, bool hidden)
{ {
mg_wgl_surface* surface = (mg_wgl_surface*)interface; mg_wgl_surface* surface = (mg_wgl_surface*)interface;
if(surface) mp_layer_set_hidden(&surface->layer, hidden);
{
ShowWindow(surface->hWnd, hidden ? SW_HIDE : SW_NORMAL);
}
} }
bool mg_wgl_surface_get_hidden(mg_surface_data* interface) bool mg_wgl_surface_get_hidden(mg_surface_data* interface)
{ {
bool hidden = false;
mg_wgl_surface* surface = (mg_wgl_surface*)interface; mg_wgl_surface* surface = (mg_wgl_surface*)interface;
if(surface) return(mp_layer_get_hidden(&surface->layer));
{
hidden = !IsWindowVisible(surface->hWnd);
}
return(hidden);
} }
void* mg_wgl_get_proc(const char* name) void* mg_wgl_get_proc(const char* name)
{ {
void* p = wglGetProcAddress(name); void* p = wglGetProcAddress(name);
@ -242,11 +205,6 @@ void* mg_wgl_get_proc(const char* name)
return(p); return(p);
} }
LRESULT mg_wgl_surface_window_proc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam)
{
return(DefWindowProc(windowHandle, message, wParam, lParam));
}
mg_surface mg_wgl_surface_create_for_window(mp_window window) mg_surface mg_wgl_surface_create_for_window(mp_window window)
{ {
mg_surface surfaceHandle = mg_surface_nil(); mg_surface surfaceHandle = mg_surface_nil();
@ -256,30 +214,22 @@ mg_surface mg_wgl_surface_create_for_window(mp_window window)
{ {
wgl_init(); wgl_init();
//NOTE(martin): create a child window for the surface //NOTE: fill surface data and load api
WNDCLASS childWindowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, mg_wgl_surface* surface = malloc_type(mg_wgl_surface);
.lpfnWndProc = mg_wgl_surface_window_proc,
.hInstance = GetModuleHandleW(NULL),
.lpszClassName = "wgl_surface_window_class",
.hCursor = LoadCursor(0, IDC_ARROW)};
if(!RegisterClass(&childWindowClass)) surface->interface.backend = MG_BACKEND_GL;
{ surface->interface.destroy = mg_wgl_surface_destroy;
//TODO: error surface->interface.prepare = mg_wgl_surface_prepare;
} surface->interface.present = mg_wgl_surface_present;
surface->interface.swapInterval = mg_wgl_surface_swap_interval;
surface->interface.contentsScaling = mg_wgl_surface_contents_scaling;
surface->interface.getFrame = mg_wgl_surface_get_frame;
surface->interface.setFrame = mg_wgl_surface_set_frame;
surface->interface.getHidden = mg_wgl_surface_get_hidden;
surface->interface.setHidden = mg_wgl_surface_set_hidden;
mp_rect frame = mp_window_get_content_rect(window); mp_layer_init_for_window(&surface->layer, windowData);
surface->hDC = GetDC(surface->layer.hWnd);
u32 dpi = GetDpiForWindow(windowData->win32.hWnd);
vec2 contentsScaling = (vec2){(float)dpi/96., (float)dpi/96.};
HWND childWindow = CreateWindow("wgl_surface_window_class", "Test",
WS_CHILD|WS_VISIBLE,
0, 0, frame.w*contentsScaling.x, frame.h*contentsScaling.y,
windowData->win32.hWnd,
0,
childWindowClass.hInstance,
0);
//NOTE(martin): create the pixel format and gl context //NOTE(martin): create the pixel format and gl context
PIXELFORMATDESCRIPTOR pixelFormatDesc = PIXELFORMATDESCRIPTOR pixelFormatDesc =
@ -312,17 +262,16 @@ mg_surface mg_wgl_surface_create_for_window(mp_window window)
WGL_STENCIL_BITS_ARB, 8, WGL_STENCIL_BITS_ARB, 8,
0}; 0};
HDC hDC = GetDC(childWindow);
u32 numFormats = 0; u32 numFormats = 0;
int pixelFormat = 0; int pixelFormat = 0;
wglChoosePixelFormatARB(hDC, pixelFormatAttrs, 0, 1, &pixelFormat, &numFormats); wglChoosePixelFormatARB(surface->hDC, pixelFormatAttrs, 0, 1, &pixelFormat, &numFormats);
if(!pixelFormat) if(!pixelFormat)
{ {
//TODO: error //TODO: error
} }
SetPixelFormat(hDC, pixelFormat, &pixelFormatDesc); SetPixelFormat(surface->hDC, pixelFormat, &pixelFormatDesc);
int contextAttrs[] = { int contextAttrs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 4, WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
@ -330,39 +279,19 @@ mg_surface mg_wgl_surface_create_for_window(mp_window window)
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0}; 0};
HGLRC glContext = wglCreateContextAttribsARB(hDC, __mgWGLDummyContext.glContext, contextAttrs); surface->glContext = wglCreateContextAttribsARB(surface->hDC, __mgWGLDummyContext.glContext, contextAttrs);
if(!glContext) if(!surface->glContext)
{ {
//TODO error //TODO error
int error = GetLastError(); int error = GetLastError();
printf("error: %i\n", error); printf("error: %i\n", error);
} }
//NOTE: make gl context current //NOTE: make gl context current and load api
wglMakeCurrent(hDC, glContext); wglMakeCurrent(surface->hDC, surface->glContext);
wglSwapIntervalEXT(1); wglSwapIntervalEXT(1);
mg_gl_load_gl43(&surface->api, mg_wgl_get_proc);
//NOTE: fill surface data and load api
mg_wgl_surface* surface = malloc_type(mg_wgl_surface);
surface->interface.backend = MG_BACKEND_GL;
surface->interface.destroy = mg_wgl_surface_destroy;
surface->interface.prepare = mg_wgl_surface_prepare;
surface->interface.present = mg_wgl_surface_present;
surface->interface.swapInterval = mg_wgl_surface_swap_interval;
surface->interface.contentsScaling = mg_wgl_surface_contents_scaling;
surface->interface.getFrame = mg_wgl_surface_get_frame;
surface->interface.setFrame = mg_wgl_surface_set_frame;
surface->interface.getHidden = mg_wgl_surface_get_hidden;
surface->interface.setHidden = mg_wgl_surface_set_hidden;
surface->hWnd = childWindow;
surface->hDC = hDC;
surface->glContext = glContext;
surface->contentsScaling = contentsScaling;
mg_gl_load_gl43(&surface->api, mg_wgl_get_proc);
surfaceHandle = mg_surface_alloc_handle((mg_surface_data*)surface); surfaceHandle = mg_surface_alloc_handle((mg_surface_data*)surface);
} }

View File

@ -777,6 +777,94 @@ mp_rect mp_window_get_content_rect(mp_window window)
return(rect); return(rect);
} }
///////////////////////////////////////////
void mp_layer_init_for_window(mp_layer* layer, mp_window_data* window)
{
//NOTE(martin): create a child window for the surface
WNDCLASS layerWindowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
.lpfnWndProc = DefWindowProc,
.hInstance = GetModuleHandleW(NULL),
.lpszClassName = "layer_window_class",
.hCursor = LoadCursor(0, IDC_ARROW)};
RegisterClass(&layerWindowClass);
RECT parentRect;
GetClientRect(window->win32.hWnd, &parentRect);
int width = parentRect.right - parentRect.left;
int height = parentRect.bottom - parentRect.top;
layer->hWnd = CreateWindow("layer_window_class", "layer",
WS_CHILD | WS_VISIBLE,
0, 0, width, height,
window->win32.hWnd,
0,
layerWindowClass.hInstance,
0);
}
void mp_layer_cleanup(mp_layer* layer)
{
DestroyWindow(layer->hWnd);
}
void* mp_layer_native_surface(mp_layer* layer)
{
return((void*)layer->hWnd);
}
vec2 mp_layer_contents_scaling(mp_layer* layer)
{
u32 dpi = GetDpiForWindow(layer->hWnd);
vec2 contentsScaling = (vec2){(float)dpi/96., (float)dpi/96.};
return(contentsScaling);
}
mp_rect mp_layer_get_frame(mp_layer* layer)
{
RECT rect = {0};
GetClientRect(layer->hWnd, &rect);
vec2 scale = mp_layer_contents_scaling(layer);
mp_rect res = {rect.left/scale.x,
rect.bottom/scale.y,
(rect.right - rect.left)/scale.x,
(rect.bottom - rect.top)/scale.y};
return(res);
}
void mp_layer_set_frame(mp_layer* layer, mp_rect frame)
{
HWND parent = GetParent(layer->hWnd);
RECT parentContentRect;
GetClientRect(parent, &parentContentRect);
int parentHeight = parentContentRect.bottom - parentContentRect.top;
vec2 scale = mp_layer_contents_scaling(layer);
SetWindowPos(layer->hWnd,
HWND_TOP,
frame.x * scale.x,
parentHeight - (frame.y + frame.h) * scale.y,
frame.w * scale.x,
frame.h * scale.y,
SWP_NOACTIVATE | SWP_NOZORDER);
}
void mp_layer_set_hidden(mp_layer* layer, bool hidden)
{
ShowWindow(layer->hWnd, hidden ? SW_HIDE : SW_NORMAL);
}
bool mp_layer_get_hidden(mp_layer* layer)
{
bool hidden = !IsWindowVisible(layer->hWnd);
return(hidden);
}
/////////////////////////////////////////// WIP /////////////////////////////////////////////// /////////////////////////////////////////// WIP ///////////////////////////////////////////////
//TODO: this is thrown here for a quick test. We should: //TODO: this is thrown here for a quick test. We should:
// - check for errors // - check for errors

View File

@ -16,12 +16,17 @@
#define UNICODE #define UNICODE
#include<windows.h> #include<windows.h>
typedef struct win32_window_data typedef struct win32_window_data
{ {
HWND hWnd; HWND hWnd;
} win32_window_data; } win32_window_data;
typedef struct mp_layer
{
HWND hWnd;
} mp_layer;
#define MP_PLATFORM_WINDOW_DATA win32_window_data win32; #define MP_PLATFORM_WINDOW_DATA win32_window_data win32;
typedef struct win32_app_data typedef struct win32_app_data

View File

@ -4,27 +4,24 @@ Overview
[.] Make backend selection easier [.] Make backend selection easier
[.] option macros to select backend-specific APIs to include when building an app [.] option macros to select backend-specific APIs to include when building an app
(ie, include gl_api.h when using gl backend) (ie, include gl_api.h when using gl backend)
[ ] error on bad option macro combinations [/] error on bad option macro combinations
[ ] write doc about backend option macros [/] write doc about backend option macros
[>] Image API and backend [?] could have an internal, per-platform mp_layer struct that handles resizing/hiding etc...
[ ] Build image atlas on top -> avoid duplication of frame/hiding and duplication of egl code.
[ ] Baked fonts? [/] EGL surfaces: See how we can isolate platform-specific stuff and just deal with one egl impl...
[x] Allow different versions of GL/GLES to co-exist
[/] Allow selecting version of GL/GLES context when creating surface
- pass/set attributes when creating surface?
[ ] Check that we can make GLES and GL surfaces co-exist in the app [ ] Check that we can make GLES and GL surfaces co-exist in the app
[ ] Allow controlling surface overlaying [ ] Allow controlling surface overlaying
[/] could have an internal, per-platform mp_layer struct that handles resizing/hiding etc... [/] Allow selecting version of GL/GLES context when creating surface
-> avoid duplication of frame/hiding and duplication of egl code. - pass/set attributes when creating surface?
[/] EGL surfaces: See how we can isolate platform-specific stuff and just deal with one egl impl...
[/] Automatic surface resizing [/] Automatic surface resizing
[/] Keep dummy window/dummy context around for gl context creation, and don't reload wgl functions every time
[!] Make linking with libEGL optional, even if EGL backend is compiled in milepost? [!] Make linking with libEGL optional, even if EGL backend is compiled in milepost?
- use dead_strip_dylib on osx?
- use /DELAYLOAD:lib on windows?
[!] Bundle examples with their own resources?? (e.g. avoiding clashes in metal libs files in bin directory) [!] Bundle examples with their own resources?? (e.g. avoiding clashes in metal libs files in bin directory)
[!] Sort out gles contents scaling for high dpi on osx [!] Sort out gles contents scaling for high dpi on osx
@ -33,10 +30,13 @@ Overview
[!] Fix canvas perf issue on OSX [!] Fix canvas perf issue on OSX
[>] Image API and backend
[ ] Build image atlas on top
[ ] Baked fonts?
[ ] Delegated drawing API+Impl [ ] Delegated drawing API+Impl
[ ] Make building apps simpler [ ] Make building apps simpler
[ ] single include path [ ] single include path
[ ] script for embedding dependencies / create app bundle [ ] script for embedding dependencies / create app bundle