From 5935635433a96e89f7ac856101026d501222a893 Mon Sep 17 00:00:00 2001 From: Igor Mitsyanko Date: Thu, 15 Nov 2012 17:41:23 +0400 Subject: [PATCH] YaGL: implement EGL WGL driver to support Windows hosts Signed-off-by: Igor Mitsyanko --- hw/yagl_drivers/egl_wgl/yagl_egl_wgl.c | 1138 +++++++++++++++++++++++- 1 file changed, 1135 insertions(+), 3 deletions(-) diff --git a/hw/yagl_drivers/egl_wgl/yagl_egl_wgl.c b/hw/yagl_drivers/egl_wgl/yagl_egl_wgl.c index 5519795834..6a6e256b45 100644 --- a/hw/yagl_drivers/egl_wgl/yagl_egl_wgl.c +++ b/hw/yagl_drivers/egl_wgl/yagl_egl_wgl.c @@ -1,24 +1,1156 @@ #include -#include #include +#include +#include +#include #include "yagl_egl_driver.h" #include "yagl_dyn_lib.h" #include "yagl_log.h" +#include "yagl_egl_native_config.h" +#include "yagl_egl_surface_attribs.h" +#include "yagl_process.h" #include "yagl_tls.h" #include "yagl_thread.h" +#define YAGL_EGL_WGL_WIN_CLASS "YaGLwinClass" + +#define YAGL_EGL_WGL_ENTER(func, format, ...) \ + YAGL_LOG_FUNC_ENTER((egl_wgl_ts ? egl_wgl_ts->ps->id : driver_ps->ps->id), \ + (egl_wgl_ts ? egl_wgl_ts->id : 0), \ + func, format,##__VA_ARGS__) + +/* Get a core WGL function address */ +#define YAGL_EGL_WGL_GET_PROC(proc_type, proc_name) \ + do { \ + egl_wgl->proc_name = \ + (proc_type)yagl_dyn_lib_get_sym(egl_driver->dyn_lib, #proc_name); \ + if (!egl_wgl->proc_name) { \ + YAGL_LOG_ERROR("%s not found!", #proc_name); \ + goto fail; \ + } \ + YAGL_LOG_TRACE("Got %s address", #proc_name); \ + } while (0) + +/* Get a mandatory WGL extension function pointer */ +#define YAGL_EGL_WGL_GET_EXT_PROC(ext_name, proc_name, proc_type) \ + do { \ + if (!yagl_egl_wgl_search_ext(ext_str, #ext_name)) { \ + YAGL_LOG_ERROR("%s extension not supported", #ext_name); \ + goto out; \ + } \ + egl_wgl->proc_name = (proc_type) \ + egl_wgl->wglGetProcAddress((LPCSTR)#proc_name); \ + if (!egl_wgl->proc_name) { \ + YAGL_LOG_ERROR("Mandatory function %s not found", #proc_name); \ + goto out; \ + } \ + YAGL_LOG_TRACE("Got %s address", #proc_name); \ + } while (0) + +static YAGL_DEFINE_TLS(struct yagl_thread_state *, egl_wgl_ts); + +typedef HGLRC (WINAPI *WGLCREATECONTEXTPROC)(HDC hdl); +typedef BOOL (WINAPI *WGLDELETECONTEXTPROC)(HGLRC hdl); +typedef PROC (WINAPI *WGLGETPROCADDRESSPROC)(LPCSTR sym); +typedef BOOL (WINAPI *WGLMAKECURRENTPROC)(HDC dev_ctx, HGLRC rend_ctx); +typedef BOOL (WINAPI *WGLSHARELISTSPROC)(HGLRC ctx1, HGLRC ctx2); + +typedef struct YaglEglWglDriverPS { + struct yagl_egl_driver_ps base; +} YaglEglWglDriverPS; + +typedef struct YaglEglWglDpy { + HWND win; + GHashTable *dc_table; + HDC *dc_arr; + gint *cfgids; + unsigned cfgid_cnt; + unsigned max_cfgid; +} YaglEglWglDpy; + +typedef struct YaglEglWglSG { + HGLRC shared_glc; + unsigned ref_cnt; +} YaglEglWglSG; + +typedef struct YaglEglWglCtx { + HGLRC glc; + YaglEglWglSG *share_group; +} YaglEglWglCtx; + +typedef struct YaglEglWglDriver { + struct yagl_egl_driver base; + + WGLCREATECONTEXTPROC wglCreateContext; + WGLDELETECONTEXTPROC wglDeleteContext; + WGLGETPROCADDRESSPROC wglGetProcAddress; + WGLMAKECURRENTPROC wglMakeCurrent; + WGLSHARELISTSPROC wglShareLists; + + /* WGL extensions */ + PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT; + PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB; + PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB; + PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; + PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB; + PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDCARB; + PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDCARB; + PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbufferARB; + PFNWGLMAKECONTEXTCURRENTARBPROC wglMakeContextCurrentARB; +} YaglEglWglDriver; + +static inline HWND yagl_egl_wgl_dummy_win_create(void) +{ + return CreateWindow(YAGL_EGL_WGL_WIN_CLASS, "YaGLwin", + WS_DISABLED | WS_POPUP, 0, 0, 1, 1, NULL, NULL, 0, 0); +} + +static inline bool yagl_egl_wgl_dc_set_def_pixfmt(HDC dc) +{ + INT pixfmt_idx; + PIXELFORMATDESCRIPTOR pixfmt = { + .nSize = sizeof(PIXELFORMATDESCRIPTOR), + .nVersion = 1, + .dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + .iPixelType = PFD_TYPE_RGBA, + .cColorBits = 32, + .cDepthBits = 24, + .cStencilBits = 8, + .iLayerType = PFD_MAIN_PLANE, + }; + + pixfmt_idx = ChoosePixelFormat(dc, &pixfmt); + + if (pixfmt_idx == 0) { + return false; + } + + if (SetPixelFormat(dc, pixfmt_idx, &pixfmt) == FALSE) { + return false; + } + + return true; +} + +static void yagl_egl_wgl_thread_init(struct yagl_egl_driver_ps *driver_ps, + struct yagl_thread_state *ts) +{ + egl_wgl_ts = ts; + + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_thread_init, NULL); + + YAGL_LOG_FUNC_EXIT(NULL); +} + +static EGLNativeDisplayType +yagl_egl_wgl_display_create(struct yagl_egl_driver_ps *driver_ps) +{ + YaglEglWglDpy *dpy; + + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_display_create, NULL); + + dpy = g_try_new0(YaglEglWglDpy, 1); + + if (!dpy) { + goto fail; + } + + dpy->win = yagl_egl_wgl_dummy_win_create(); + + if (!dpy->win) { + g_free(dpy); + goto fail; + } + + dpy->dc_table = g_hash_table_new(g_int_hash, g_int_equal); + + YAGL_LOG_FUNC_EXIT("Display created: %p", dpy); + + return (EGLNativeDisplayType)dpy; + +fail: + YAGL_LOG_FUNC_EXIT("Display creation failed"); + + return NULL; +} + +static gboolean yagl_egl_wgl_cfgdc_free(gpointer key, + gpointer value, + gpointer usrdata) +{ + HWND win = *((HWND *)usrdata); + HDC dc = *((HDC *)value); + + ReleaseDC(win, dc); + + return TRUE; +} + +static void yagl_egl_wgl_display_destroy(struct yagl_egl_driver_ps *driver_ps, + EGLNativeDisplayType egl_dpy) +{ + YaglEglWglDpy *dpy = (YaglEglWglDpy *)egl_dpy; + + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_display_destroy, "%p", dpy); + + g_hash_table_foreach_remove(dpy->dc_table, + yagl_egl_wgl_cfgdc_free, + &dpy->win); + + g_hash_table_destroy(dpy->dc_table); + + DestroyWindow(dpy->win); + + g_free(dpy->cfgids); + g_free(dpy->dc_arr); + g_free(dpy); + + YAGL_LOG_FUNC_EXIT(NULL); +} + +static bool yagl_egl_wgl_config_fill(struct yagl_egl_driver_ps *driver_ps, + HDC dc, + struct yagl_egl_native_config *cfg, + int fmt_idx) +{ + YaglEglWglDriver *egl_wgl = (YaglEglWglDriver *)(driver_ps->driver); + PIXELFORMATDESCRIPTOR *pix_fmt = NULL; + bool filled = false; + + enum { + YAGL_WGL_PIXEL_TYPE = 0, + YAGL_WGL_DRAW_TO_PBUFFER, + YAGL_WGL_DOUBLE_BUFFER, + YAGL_WGL_SUPPORT_OPENGL, + YAGL_WGL_ACCELERATION, + YAGL_WGL_NEED_PALETTE, + YAGL_WGL_TRANSPARENT, + YAGL_WGL_TRANSPARENT_RED_VALUE, + YAGL_WGL_TRANSPARENT_GREEN_VALUE, + YAGL_WGL_TRANSPARENT_BLUE_VALUE, + YAGL_WGL_COLOR_BITS, + YAGL_WGL_RED_BITS, + YAGL_WGL_GREEN_BITS, + YAGL_WGL_BLUE_BITS, + YAGL_WGL_ALPHA_BITS, + YAGL_WGL_DEPTH_BITS, + YAGL_WGL_STENCIL_BITS, + YAGL_WGL_MAX_PBUFFER_PIXELS, + YAGL_WGL_MAX_PBUFFER_WIDTH, + YAGL_WGL_MAX_PBUFFER_HEIGHT, + YAGL_WGL_NUMBER_OVERLAYS, + YAGL_WGL_NUMBER_UNDERLAYS, + YAGL_WGL_NUM_OF_QUERY_ATTRIBS, + }; + + const int query_list[YAGL_WGL_NUM_OF_QUERY_ATTRIBS] = { + [YAGL_WGL_PIXEL_TYPE] = WGL_PIXEL_TYPE_ARB, + [YAGL_WGL_DRAW_TO_PBUFFER] = WGL_DRAW_TO_PBUFFER_ARB, + [YAGL_WGL_DOUBLE_BUFFER] = WGL_DOUBLE_BUFFER_ARB, + [YAGL_WGL_SUPPORT_OPENGL] = WGL_SUPPORT_OPENGL_ARB, + [YAGL_WGL_ACCELERATION] = WGL_ACCELERATION_ARB, + [YAGL_WGL_NEED_PALETTE] = WGL_NEED_PALETTE_ARB, + [YAGL_WGL_TRANSPARENT] = WGL_TRANSPARENT_ARB, + [YAGL_WGL_TRANSPARENT_RED_VALUE] = WGL_TRANSPARENT_RED_VALUE_ARB, + [YAGL_WGL_TRANSPARENT_GREEN_VALUE] = WGL_TRANSPARENT_GREEN_VALUE_ARB, + [YAGL_WGL_TRANSPARENT_BLUE_VALUE] = WGL_TRANSPARENT_BLUE_VALUE_ARB, + [YAGL_WGL_COLOR_BITS] = WGL_COLOR_BITS_ARB, + [YAGL_WGL_RED_BITS] = WGL_RED_BITS_ARB, + [YAGL_WGL_GREEN_BITS] = WGL_GREEN_BITS_ARB, + [YAGL_WGL_BLUE_BITS] = WGL_BLUE_BITS_ARB, + [YAGL_WGL_ALPHA_BITS] = WGL_ALPHA_BITS_ARB, + [YAGL_WGL_DEPTH_BITS] = WGL_DEPTH_BITS_ARB, + [YAGL_WGL_STENCIL_BITS] = WGL_STENCIL_BITS_ARB, + [YAGL_WGL_MAX_PBUFFER_PIXELS] = WGL_MAX_PBUFFER_PIXELS_ARB, + [YAGL_WGL_MAX_PBUFFER_WIDTH] = WGL_MAX_PBUFFER_WIDTH_ARB, + [YAGL_WGL_MAX_PBUFFER_HEIGHT] = WGL_MAX_PBUFFER_HEIGHT_ARB, + [YAGL_WGL_NUMBER_OVERLAYS] = WGL_NUMBER_OVERLAYS_ARB, + [YAGL_WGL_NUMBER_UNDERLAYS] = WGL_NUMBER_UNDERLAYS_ARB, + }; + + int attr_vals[YAGL_WGL_NUM_OF_QUERY_ATTRIBS]; + + if (egl_wgl->wglGetPixelFormatAttribivARB(dc, fmt_idx, 0, + YAGL_WGL_NUM_OF_QUERY_ATTRIBS, + query_list, attr_vals) == FALSE) { + goto out; + } + + if (attr_vals[YAGL_WGL_PIXEL_TYPE] != WGL_TYPE_RGBA_ARB || + attr_vals[YAGL_WGL_DRAW_TO_PBUFFER] != TRUE || + attr_vals[YAGL_WGL_DOUBLE_BUFFER] != TRUE || + attr_vals[YAGL_WGL_SUPPORT_OPENGL] != TRUE || + attr_vals[YAGL_WGL_ACCELERATION] != WGL_FULL_ACCELERATION_ARB || + attr_vals[YAGL_WGL_NEED_PALETTE] != FALSE) { + goto out; + } + + /* This structure is used as a config driver data to pass it to + * ChoosePixelFormat later */ + pix_fmt = g_try_new0(PIXELFORMATDESCRIPTOR, 1); + if (!pix_fmt) { + goto out; + } + + if (!DescribePixelFormat(dc, fmt_idx, + sizeof(PIXELFORMATDESCRIPTOR), + pix_fmt)) { + g_free(pix_fmt); + goto out; + } + + yagl_egl_native_config_init(cfg); + + if (attr_vals[YAGL_WGL_TRANSPARENT] == TRUE) { + cfg->transparent_type = EGL_TRANSPARENT_RGB; + cfg->trans_red_val = attr_vals[YAGL_WGL_TRANSPARENT_RED_VALUE]; + cfg->trans_green_val = attr_vals[YAGL_WGL_TRANSPARENT_GREEN_VALUE]; + cfg->trans_blue_val = attr_vals[YAGL_WGL_TRANSPARENT_BLUE_VALUE]; + } else { + cfg->transparent_type = EGL_NONE; + } + + cfg->config_id = fmt_idx; + cfg->buffer_size = attr_vals[YAGL_WGL_COLOR_BITS]; + cfg->red_size = attr_vals[YAGL_WGL_RED_BITS]; + cfg->green_size = attr_vals[YAGL_WGL_GREEN_BITS]; + cfg->blue_size = attr_vals[YAGL_WGL_BLUE_BITS]; + cfg->alpha_size = attr_vals[YAGL_WGL_ALPHA_BITS]; + cfg->depth_size = attr_vals[YAGL_WGL_DEPTH_BITS]; + cfg->stencil_size = attr_vals[YAGL_WGL_STENCIL_BITS]; + cfg->max_pbuffer_width = attr_vals[YAGL_WGL_MAX_PBUFFER_WIDTH]; + cfg->max_pbuffer_height = attr_vals[YAGL_WGL_MAX_PBUFFER_HEIGHT]; + cfg->max_pbuffer_size = attr_vals[YAGL_WGL_MAX_PBUFFER_PIXELS]; + cfg->native_visual_type = EGL_NONE; + cfg->native_visual_id = 0; + cfg->caveat = EGL_NONE; + cfg->frame_buffer_level = 0; + cfg->samples_per_pixel = 0; + cfg->max_swap_interval = 10; + cfg->min_swap_interval = 1; + cfg->driver_data = pix_fmt; + + filled = true; + +out: + return filled; +} + +static struct yagl_egl_native_config + *yagl_egl_wgl_config_enum(struct yagl_egl_driver_ps *driver_ps, + EGLNativeDisplayType egl_dpy, + int *num_configs) +{ + YaglEglWglDriver *egl_wgl = (YaglEglWglDriver *)(driver_ps->driver); + YaglEglWglDpy *dpy = (YaglEglWglDpy *)egl_dpy; + HDC dc; + struct yagl_egl_native_config *egl_configs = NULL; + UINT pixfmt_cnt = 0; + int pixfmt_cnt_max = 0, curr_fmt_idx, cfg_index = 0; + int *pixfmt_arr; + const int query_num_of_formats = WGL_NUMBER_PIXEL_FORMATS_ARB; + const int attrib_list[] = { + WGL_SUPPORT_OPENGL_ARB, TRUE, + WGL_DOUBLE_BUFFER_ARB, TRUE, + WGL_DRAW_TO_PBUFFER_ARB, TRUE, + WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, + WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, + 0, + }; + + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_config_enum, "display %p", dpy); + + *num_configs = 0; + + dc = GetDC(dpy->win); + if (!dc) { + goto out; + } + + /* Query number of pixel formats */ + if (egl_wgl->wglGetPixelFormatAttribivARB(dc, 0, 0, 1, + &query_num_of_formats, + &pixfmt_cnt_max) == FALSE || + !pixfmt_cnt_max) { + pixfmt_cnt_max = DescribePixelFormat(dc, 1, 0, NULL); + } + + if (pixfmt_cnt_max == 0) { + YAGL_LOG_ERROR("No pixel formats found"); + goto out; + } + + pixfmt_arr = g_try_new0(int, pixfmt_cnt_max); + if (!pixfmt_arr) { + goto out; + } + + if (egl_wgl->wglChoosePixelFormatARB(dc, attrib_list, NULL, + pixfmt_cnt_max, + pixfmt_arr, + &pixfmt_cnt) == FALSE) { + pixfmt_cnt = pixfmt_cnt_max; + + for (curr_fmt_idx = 0; curr_fmt_idx < pixfmt_cnt; ++curr_fmt_idx) { + pixfmt_arr[curr_fmt_idx] = curr_fmt_idx + 1; + } + } + + YAGL_LOG_DEBUG("got %d pixel formats", pixfmt_cnt); + + egl_configs = g_try_new0(struct yagl_egl_native_config, pixfmt_cnt); + if (!egl_configs) { + g_free(pixfmt_arr); + goto out; + } + + for (curr_fmt_idx = 0; curr_fmt_idx < pixfmt_cnt; ++curr_fmt_idx) { + if (yagl_egl_wgl_config_fill(driver_ps, dc, + &egl_configs[cfg_index], + pixfmt_arr[curr_fmt_idx])) { + YAGL_LOG_TRACE("Added config with pixfmt=%d", + pixfmt_arr[curr_fmt_idx]); + ++cfg_index; + } + } + + if (cfg_index < pixfmt_cnt) { + egl_configs = g_renew(struct yagl_egl_native_config, + egl_configs, + cfg_index); + } + + *num_configs = cfg_index; + + dpy->cfgids = g_new0(gint, cfg_index); + dpy->dc_arr = g_new0(HDC, cfg_index); + dpy->max_cfgid = cfg_index - 1; + + g_free(pixfmt_arr); + +out: + if (dc) { + ReleaseDC(dpy->win, dc); + } + + YAGL_LOG_INFO("WGL returned %d configs, %d are usable", + pixfmt_cnt, cfg_index); + + YAGL_LOG_FUNC_EXIT("Enumerated %d configs", cfg_index); + + return egl_configs; +} + +static void yagl_egl_wgl_config_cleanup(struct yagl_egl_driver_ps *driver_ps, + EGLNativeDisplayType dpy, + struct yagl_egl_native_config *cfg) +{ + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_config_cleanup, + "dpy = %p, cfg = %d", + dpy, cfg->config_id); + + g_free(cfg->driver_data); + cfg->driver_data = NULL; + + YAGL_LOG_FUNC_EXIT(NULL); +} + +static HDC yagl_egl_wgl_dc_by_cfgid_get(YaglEglWglDpy *dpy, + const struct yagl_egl_native_config *cfg) +{ + HDC dc; + HDC *dc_p; + + dc_p = g_hash_table_lookup(dpy->dc_table, &cfg->config_id); + + if (dc_p) { + dc = *dc_p; + goto out; + } + + assert(dpy->cfgid_cnt <= dpy->max_cfgid); + + dc = GetDC(dpy->win); + if (!dc) { + goto out; + } + + if (!SetPixelFormat(dc, cfg->config_id, + (PIXELFORMATDESCRIPTOR *)cfg->driver_data)) { + ReleaseDC(dpy->win, dc); + dc = NULL; + goto out; + } + + dpy->dc_arr[dpy->cfgid_cnt] = dc; + dpy->cfgids[dpy->cfgid_cnt] = cfg->config_id; + + g_hash_table_insert(dpy->dc_table, + &dpy->cfgids[dpy->cfgid_cnt], + &dpy->dc_arr[dpy->cfgid_cnt]); + + ++dpy->cfgid_cnt; + +out: + return dc; +} + +static void yagl_egl_wgl_sharegrp_release(YaglEglWglDriver *egl_wgl, + YaglEglWglSG *share_group) +{ + assert(share_group->ref_cnt > 0); + + if (--share_group->ref_cnt == 0) { + egl_wgl->wglDeleteContext(share_group->shared_glc); + g_free(share_group); + } +} + +static inline void yagl_egl_wgl_sharegrp_acquire(YaglEglWglSG *share_group) +{ + ++share_group->ref_cnt; +} + +static EGLContext yagl_egl_wgl_context_create(struct yagl_egl_driver_ps *driver_ps, + EGLNativeDisplayType egl_dpy, + const struct yagl_egl_native_config *cfg, + yagl_client_api client_api, + EGLContext share_context) +{ + YaglEglWglDriver *egl_wgl = (YaglEglWglDriver *)(driver_ps->driver); + YaglEglWglDpy *dpy = (YaglEglWglDpy *)egl_dpy; + YaglEglWglCtx *egl_wgl_ctx; + HDC dc; + + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_context_create, + "dpy = %p, api = %u, share_context = %p, cfgid=%d", + dpy, client_api, share_context, cfg->config_id); + + egl_wgl_ctx = g_try_new0(YaglEglWglCtx, 1); + if (!egl_wgl_ctx) { + goto fail; + } + + dc = yagl_egl_wgl_dc_by_cfgid_get(dpy, cfg); + if (!dc) { + YAGL_LOG_ERROR("failed to get DC with cfgid=%d", cfg->config_id); + goto fail; + } + + if (share_context != EGL_NO_CONTEXT) { + egl_wgl_ctx->share_group = + ((YaglEglWglCtx *)share_context)->share_group; + } else { + egl_wgl_ctx->share_group = g_try_new0(YaglEglWglSG, 1); + + if (!egl_wgl_ctx->share_group) { + goto fail; + } + + egl_wgl_ctx->share_group->shared_glc = egl_wgl->wglCreateContext(dc); + if (!egl_wgl_ctx->share_group->shared_glc) { + g_free(egl_wgl_ctx->share_group); + goto fail; + } + } + + yagl_egl_wgl_sharegrp_acquire(egl_wgl_ctx->share_group); + + egl_wgl_ctx->glc = egl_wgl->wglCreateContext(dc); + if (!egl_wgl_ctx->glc) { + yagl_egl_wgl_sharegrp_release(egl_wgl, egl_wgl_ctx->share_group); + goto fail; + } + + if(!egl_wgl->wglShareLists(egl_wgl_ctx->share_group->shared_glc, + egl_wgl_ctx->glc)) { + yagl_egl_wgl_sharegrp_release(egl_wgl, egl_wgl_ctx->share_group); + egl_wgl->wglDeleteContext(egl_wgl_ctx->glc); + goto fail; + } + + YAGL_LOG_FUNC_EXIT("Context created: %p (HGLRC=%p, shared=%p refcnt=%u)", + egl_wgl_ctx, egl_wgl_ctx->glc, egl_wgl_ctx->share_group->shared_glc, + egl_wgl_ctx->share_group->ref_cnt); + + return (EGLContext)egl_wgl_ctx; + +fail: + g_free(egl_wgl_ctx); + + YAGL_LOG_ERROR_WIN(); + + YAGL_LOG_FUNC_EXIT("Failed to create new context"); + + return EGL_NO_CONTEXT; +} + +static bool yagl_egl_wgl_make_current(struct yagl_egl_driver_ps *driver_ps, + EGLNativeDisplayType dc, + EGLSurface egl_draw_surf, + EGLSurface egl_read_surf, + EGLContext egl_glc) +{ + YaglEglWglDriver *egl_wgl = (YaglEglWglDriver *)(driver_ps->driver); + HDC draw_dc = NULL, read_dc = NULL; + HGLRC glc = NULL; + + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_make_current, + "dpy = %p, draw = %p, read = %p, ctx = %p", + dc, + egl_draw_surf, + egl_read_surf, + egl_glc); + + if (egl_glc != EGL_NO_CONTEXT) { + glc = ((YaglEglWglCtx *)egl_glc)->glc; + } + + if (egl_draw_surf != EGL_NO_SURFACE) { + draw_dc = egl_wgl->wglGetPbufferDCARB((HPBUFFERARB)egl_draw_surf); + + if (!draw_dc) { + goto fail; + } + } + + if (egl_read_surf != EGL_NO_SURFACE) { + if (egl_read_surf == egl_draw_surf) { + read_dc = draw_dc; + } else { + read_dc = egl_wgl->wglGetPbufferDCARB((HPBUFFERARB)egl_read_surf); + + if (!read_dc) { + goto fail; + } + } + } + + if (egl_wgl->wglMakeContextCurrentARB(draw_dc, read_dc, glc) == FALSE) { + goto fail; + } + + YAGL_LOG_FUNC_EXIT("context %p was made current", glc); + + return true; + +fail: + YAGL_LOG_ERROR_WIN(); + + if (draw_dc) { + egl_wgl->wglReleasePbufferDCARB((HPBUFFERARB)egl_draw_surf, draw_dc); + } + + if (read_dc && (egl_read_surf != egl_draw_surf)) { + egl_wgl->wglReleasePbufferDCARB((HPBUFFERARB)egl_read_surf, read_dc); + } + + YAGL_LOG_FUNC_EXIT("Failed to make context %p current", glc); + + return false; +} + +static void yagl_egl_wgl_context_destroy(struct yagl_egl_driver_ps *driver_ps, + EGLNativeDisplayType egl_dpy, + EGLContext egl_glc) +{ + YaglEglWglDriver *egl_wgl = (YaglEglWglDriver *)(driver_ps->driver); + YaglEglWglCtx *wgl_ctx = (YaglEglWglCtx *)egl_glc; + + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_context_destroy, + "dpy = %p, ctx = %p, glc=%p", + egl_dpy, wgl_ctx, wgl_ctx->glc); + + yagl_egl_wgl_sharegrp_release(egl_wgl, wgl_ctx->share_group); + + if (egl_wgl->wglDeleteContext(wgl_ctx->glc) == FALSE) { + YAGL_LOG_ERROR_WIN(); + } + + g_free(wgl_ctx); + + YAGL_LOG_FUNC_EXIT("Context destroyed"); +} + +static EGLSurface yagl_egl_wgl_pbuffer_surface_create(struct yagl_egl_driver_ps *driver_ps, + EGLNativeDisplayType egl_dpy, + const struct yagl_egl_native_config *cfg, + EGLint width, + EGLint height, + const struct yagl_egl_pbuffer_attribs *attribs) +{ + YaglEglWglDriver *egl_wgl = (YaglEglWglDriver *)(driver_ps->driver); + HDC dc = NULL; + YaglEglWglDpy * dpy = (YaglEglWglDpy *)egl_dpy; + HPBUFFERARB pbuffer; + int pbuff_attribs[] = { + WGL_PBUFFER_LARGEST_ARB, FALSE, + WGL_TEXTURE_TARGET_ARB, WGL_NO_TEXTURE_ARB, + WGL_TEXTURE_FORMAT_ARB, WGL_NO_TEXTURE_ARB, + 0 + }; + + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_pbuffer_surface_create, + "dpy = %p, width = %d, height = %d, cfgid=%d", + egl_dpy, + width, + height, + cfg->config_id); + + if (attribs->largest) { + pbuff_attribs[1] = TRUE; + } + + if (attribs->tex_target == EGL_TEXTURE_2D) { + pbuff_attribs[3] = WGL_TEXTURE_2D_ARB; + } + + switch (attribs->tex_format) { + case EGL_TEXTURE_RGB: + pbuff_attribs[5] = WGL_TEXTURE_RGB_ARB; + break; + case EGL_TEXTURE_RGBA: + pbuff_attribs[5] = WGL_TEXTURE_RGBA_ARB; + break; + } + + dc = yagl_egl_wgl_dc_by_cfgid_get(dpy, cfg); + if (!dc) { + YAGL_LOG_ERROR("failed to get DC with cfgid=%d", cfg->config_id); + goto fail; + } + + pbuffer = egl_wgl->wglCreatePbufferARB(dc, cfg->config_id, + width, height, pbuff_attribs); + + if (!pbuffer) { + goto fail; + } + + YAGL_LOG_FUNC_EXIT("Surface created: %p", pbuffer); + + return (EGLSurface)pbuffer; + +fail: + YAGL_LOG_ERROR_WIN(); + + YAGL_LOG_FUNC_EXIT("Surface creation failed"); + + return EGL_NO_SURFACE; +} + +static void yagl_egl_wgl_pbuffer_surface_destroy(struct yagl_egl_driver_ps *driver_ps, + EGLNativeDisplayType egl_dpy, + EGLSurface surf) +{ + YaglEglWglDriver *egl_wgl = (YaglEglWglDriver *)(driver_ps->driver); + HDC pbuf_dc = NULL; + + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_pbuffer_surface_destroy, + "dpy = %p, sfc = %p", + egl_dpy, + surf); + + pbuf_dc = egl_wgl->wglGetPbufferDCARB((HPBUFFERARB)surf); + + if (!pbuf_dc || + egl_wgl->wglReleasePbufferDCARB((HPBUFFERARB)surf, pbuf_dc) != 1) { + YAGL_LOG_ERROR_WIN(); + } + + if (egl_wgl->wglDestroyPbufferARB((HPBUFFERARB)surf) == FALSE) { + YAGL_LOG_ERROR_WIN(); + YAGL_LOG_FUNC_EXIT("Failed to destroy surface"); + } else { + YAGL_LOG_FUNC_EXIT("Surface destroyed"); + } +} + +static void yagl_egl_wgl_wait_native(struct yagl_egl_driver_ps *driver_ps) +{ + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_wait_native, NULL); + + YAGL_LOG_FUNC_EXIT(NULL); +} + +static void yagl_egl_wgl_thread_fini(struct yagl_egl_driver_ps *driver_ps) +{ + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_thread_fini, NULL); + + egl_wgl_ts = NULL; + + YAGL_LOG_FUNC_EXIT(NULL); +} + +static void yagl_egl_wgl_process_destroy(struct yagl_egl_driver_ps *driver_ps) +{ + YaglEglWglDriverPS *egl_glx_ps = (YaglEglWglDriverPS *)driver_ps; + + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_process_destroy, NULL); + + yagl_egl_driver_ps_cleanup(&egl_glx_ps->base); + + g_free(egl_glx_ps); + + YAGL_LOG_FUNC_EXIT(NULL); +} + +static struct yagl_egl_driver_ps + *yagl_egl_wgl_process_init(struct yagl_egl_driver *driver, + struct yagl_process_state *ps) +{ + YaglEglWglDriverPS *egl_wgl_ps = g_new0(YaglEglWglDriverPS, 1); + struct yagl_egl_driver_ps *driver_ps = &egl_wgl_ps->base; + + yagl_egl_driver_ps_init(driver_ps, driver, ps); + + YAGL_EGL_WGL_ENTER(yagl_egl_wgl_process_init, NULL); + + driver_ps->thread_init = &yagl_egl_wgl_thread_init; + driver_ps->display_create = &yagl_egl_wgl_display_create; + driver_ps->display_destroy = &yagl_egl_wgl_display_destroy; + driver_ps->config_enum = &yagl_egl_wgl_config_enum; + driver_ps->config_cleanup = &yagl_egl_wgl_config_cleanup; + driver_ps->pbuffer_surface_create = &yagl_egl_wgl_pbuffer_surface_create; + driver_ps->pbuffer_surface_destroy = &yagl_egl_wgl_pbuffer_surface_destroy; + driver_ps->context_create = &yagl_egl_wgl_context_create; + driver_ps->context_destroy = &yagl_egl_wgl_context_destroy; + driver_ps->make_current = &yagl_egl_wgl_make_current; + driver_ps->wait_native = &yagl_egl_wgl_wait_native; + driver_ps->thread_fini = &yagl_egl_wgl_thread_fini; + driver_ps->destroy = &yagl_egl_wgl_process_destroy; + + YAGL_LOG_FUNC_EXIT(NULL); + + return driver_ps; +} + +static void yagl_egl_wgl_destroy(struct yagl_egl_driver *driver) +{ + YaglEglWglDriver *egl_wgl = (YaglEglWglDriver *)driver; + + YAGL_LOG_FUNC_ENTER_NPT(yagl_egl_wgl_destroy, NULL); + + UnregisterClassA((LPCTSTR)YAGL_EGL_WGL_WIN_CLASS, NULL); + + yagl_egl_driver_cleanup(driver); + + g_free(egl_wgl); + + YAGL_LOG_FUNC_EXIT(NULL); +} + +/* + * Check if extension string 'ext_string' contains extension name 'ext_name'. + * Needed because extension name could be a substring of a longer extension + * name. + */ +static bool yagl_egl_wgl_search_ext(const gchar *ext_string, + const gchar *ext_name) +{ + const size_t name_len = strlen(ext_name); + gchar *ext = g_strstr_len(ext_string, -1, ext_name); + + while (ext) { + if (ext[name_len] == '\0' || ext[name_len] == ' ') { + break; + } + + ext += name_len; + ext = g_strstr_len(ext, -1, ext_name); + } + + return ext != NULL; +} + +/* + * A stub for YaGL window class window procedure callback + */ +static LRESULT CALLBACK yagl_egl_wgl_winproc(HWND win, UINT msg, WPARAM wparam, + LPARAM lparam) +{ + return DefWindowProcA(win, msg, wparam, lparam); +} + +/* + * Initialize pointers to WGL extension functions. + * Pointers are acquired with wglGetProcAddress() function, which works only + * in a presence of a valid openGL context. Pointers returned by + * wglGetProcAddress() are themselves context-specific, but we assume that + * if two contexts refer to the same GPU, then the function pointers pulled + * from one context will work in another. + * Note that we only need current context to ACQUIRE function pointers, + * we don't need a context to use them. + */ +static bool yagl_egl_wgl_init_ext(YaglEglWglDriver *egl_wgl) +{ + HWND win; + HDC dc = NULL; + HGLRC glc = NULL; + const char *ext_str = NULL; + bool ext_initialized = false; + + YAGL_LOG_FUNC_ENTER_NPT(yagl_egl_wgl_init_ext, NULL); + + win = yagl_egl_wgl_dummy_win_create(); + if (!win) { + goto out; + } + + dc = GetDC(win); + if (!dc) { + goto out; + } + + if (!yagl_egl_wgl_dc_set_def_pixfmt(dc)) { + goto out; + } + + glc = egl_wgl->wglCreateContext(dc); + if (!glc) { + goto out; + } + + if (!egl_wgl->wglMakeCurrent(dc, glc)) { + goto out; + } + + /* WGL extensions couldn't be queried by glGetString(), we need to use + * wglGetExtensionsStringARB or wglGetExtensionsStringEXT for this, which + * themselves are extensions */ + egl_wgl->wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) + egl_wgl->wglGetProcAddress((LPCSTR)"wglGetExtensionsStringARB"); + egl_wgl->wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) + egl_wgl->wglGetProcAddress((LPCSTR)"wglGetExtensionsStringEXT"); + + if (egl_wgl->wglGetExtensionsStringARB) { + ext_str = egl_wgl->wglGetExtensionsStringARB(dc); + } else if (egl_wgl->wglGetExtensionsStringEXT) { + ext_str = egl_wgl->wglGetExtensionsStringEXT(); + } + + if (!ext_str) { + YAGL_LOG_ERROR("Couldn't acquire WGL extensions string"); + goto out; + } + + YAGL_LOG_INFO("WGL extensions: %s", ext_str); + + YAGL_EGL_WGL_GET_EXT_PROC(WGL_ARB_pixel_format, wglGetPixelFormatAttribivARB, PFNWGLGETPIXELFORMATATTRIBIVARBPROC); + YAGL_EGL_WGL_GET_EXT_PROC(WGL_ARB_pbuffer, wglCreatePbufferARB, PFNWGLCREATEPBUFFERARBPROC); + YAGL_EGL_WGL_GET_EXT_PROC(WGL_ARB_pbuffer, wglGetPbufferDCARB, PFNWGLGETPBUFFERDCARBPROC); + YAGL_EGL_WGL_GET_EXT_PROC(WGL_ARB_pbuffer, wglReleasePbufferDCARB, PFNWGLRELEASEPBUFFERDCARBPROC); + YAGL_EGL_WGL_GET_EXT_PROC(WGL_ARB_pbuffer, wglDestroyPbufferARB, PFNWGLDESTROYPBUFFERARBPROC); + YAGL_EGL_WGL_GET_EXT_PROC(WGL_ARB_pixel_format, wglChoosePixelFormatARB, PFNWGLCHOOSEPIXELFORMATARBPROC); + YAGL_EGL_WGL_GET_EXT_PROC(WGL_ARB_make_current_read, wglMakeContextCurrentARB, PFNWGLMAKECONTEXTCURRENTARBPROC); + + ext_initialized = true; + +out: + if (glc) { + if (egl_wgl->wglMakeCurrent(NULL, NULL) == FALSE) { + YAGL_LOG_ERROR_WIN(); + } + + if (egl_wgl->wglDeleteContext(glc) == FALSE) { + YAGL_LOG_ERROR_WIN(); + } + } + + if (win) { + if (dc) { + ReleaseDC(win, dc); + } + DestroyWindow(win); + } + + YAGL_LOG_FUNC_EXIT("Extensions initialized status: %u", ext_initialized); + + return ext_initialized; +} + struct yagl_egl_driver *yagl_egl_wgl_create(void) { + YaglEglWglDriver *egl_wgl; + struct yagl_egl_driver *egl_driver; + WNDCLASSEXA yagl_win_class; + + YAGL_LOG_FUNC_ENTER_NPT(yagl_egl_wgl_create, NULL); + + egl_wgl = g_new0(YaglEglWglDriver, 1); + + egl_driver = &egl_wgl->base; + + yagl_egl_driver_init(egl_driver); + + egl_driver->dyn_lib = yagl_dyn_lib_create(); + + if (!yagl_dyn_lib_load(egl_driver->dyn_lib, "opengl32")) { + YAGL_LOG_ERROR("Loading opengl32.dll failed with error: %s", + yagl_dyn_lib_get_error(egl_driver->dyn_lib)); + goto fail; + } + + yagl_win_class.cbSize = sizeof(WNDCLASSEXA); + yagl_win_class.style = 0; + yagl_win_class.lpfnWndProc = &yagl_egl_wgl_winproc; + yagl_win_class.cbClsExtra = 0; + yagl_win_class.cbWndExtra = 0; + yagl_win_class.hInstance = NULL; + yagl_win_class.hIcon = NULL; + yagl_win_class.hCursor = NULL; + yagl_win_class.hbrBackground = NULL; + yagl_win_class.lpszMenuName = NULL; + yagl_win_class.lpszClassName = YAGL_EGL_WGL_WIN_CLASS; + yagl_win_class.hIconSm = NULL; + + if (!RegisterClassExA(&yagl_win_class)) { + goto fail; + } + + YAGL_EGL_WGL_GET_PROC(WGLCREATECONTEXTPROC, wglCreateContext); + YAGL_EGL_WGL_GET_PROC(WGLDELETECONTEXTPROC, wglDeleteContext); + YAGL_EGL_WGL_GET_PROC(WGLGETPROCADDRESSPROC, wglGetProcAddress); + YAGL_EGL_WGL_GET_PROC(WGLMAKECURRENTPROC, wglMakeCurrent); + YAGL_EGL_WGL_GET_PROC(WGLSHARELISTSPROC, wglShareLists); + + egl_driver->process_init = &yagl_egl_wgl_process_init; + egl_driver->destroy = &yagl_egl_wgl_destroy; + + if (!yagl_egl_wgl_init_ext(egl_wgl)) { + goto fail; + } + + YAGL_LOG_FUNC_EXIT("EGL_WGL driver created (%p)", egl_driver); + + return egl_driver; + +fail: + yagl_egl_driver_cleanup(egl_driver); + g_free(egl_wgl); + + YAGL_LOG_ERROR_WIN(); + + YAGL_LOG_FUNC_EXIT("EGL_WGL driver creation failed"); + return NULL; } +static void *yagl_egl_wgl_extaddr_get(struct yagl_dyn_lib *dyn_lib, + const char *sym) +{ + static WGLGETPROCADDRESSPROC wgl_get_procaddr_fn = NULL; + static WGLCREATECONTEXTPROC wgl_create_context_fn = NULL; + static WGLMAKECURRENTPROC wgl_make_current_fn = NULL; + static WGLDELETECONTEXTPROC wgl_delete_context_fn = NULL; + void *proc = NULL; + HWND win = NULL; + HDC dc = NULL; + HGLRC glc; + + YAGL_LOG_FUNC_ENTER_NPT(yagl_egl_wgl_extaddr_get, "%s", sym); + + if (!wgl_get_procaddr_fn) { + wgl_get_procaddr_fn = (WGLGETPROCADDRESSPROC) + yagl_dyn_lib_get_sym(dyn_lib, "wglGetProcAddress"); + } + + if (!wgl_create_context_fn) { + wgl_create_context_fn = (WGLCREATECONTEXTPROC) + yagl_dyn_lib_get_sym(dyn_lib, "wglCreateContext"); + } + + if (!wgl_make_current_fn) { + wgl_make_current_fn = (WGLMAKECURRENTPROC) + yagl_dyn_lib_get_sym(dyn_lib, "wglMakeCurrent"); + } + + if (!wgl_delete_context_fn) { + wgl_delete_context_fn = (WGLDELETECONTEXTPROC) + yagl_dyn_lib_get_sym(dyn_lib, "wglDeleteContext"); + } + + if (!wgl_get_procaddr_fn || !wgl_create_context_fn || + !wgl_make_current_fn || !wgl_delete_context_fn) { + goto out; + } + + /* To use wglGetProcAddress, create a temporary openGL context + * and make it current */ + + win = yagl_egl_wgl_dummy_win_create(); + if (!win) { + goto out; + } + + dc = GetDC(win); + if (!dc) { + goto out; + } + + /* We need to set pixel format of dc before we can create GL context */ + if (!yagl_egl_wgl_dc_set_def_pixfmt(dc)) { + goto out; + } + + glc = wgl_create_context_fn(dc); + if (!glc) { + goto out; + } + + if (wgl_make_current_fn(dc, glc) == FALSE) { + wgl_make_current_fn(NULL, NULL); + wgl_delete_context_fn(glc); + goto out; + } + + proc = (void *)wgl_get_procaddr_fn((LPCSTR)sym); + + if (wgl_make_current_fn(NULL, NULL) == FALSE) { + YAGL_LOG_ERROR_WIN(); + } + + if (wgl_delete_context_fn(glc) == FALSE) { + YAGL_LOG_ERROR_WIN(); + } + +out: + if (!proc) { + YAGL_LOG_ERROR_WIN(); + } + + if (win) { + if (dc) { + ReleaseDC(win, dc); + } + DestroyWindow(win); + } + + YAGL_LOG_FUNC_EXIT(NULL); + + return proc; +} + +/* GetProcAddress only works for core OpenGL v.1.1 functions, while + * wglGetProcAddress will fail for <=v1.1 openGL functions, so we need them + * both */ void *yagl_egl_wgl_procaddr_get(struct yagl_dyn_lib *dyn_lib, const char *sym) { void *proc = NULL; - YAGL_LOG_FUNC_ENTER_NPT(yagl_egl_wgl_get_procaddr, NULL); + YAGL_LOG_FUNC_ENTER_NPT(yagl_egl_wgl_get_procaddr, + "Retrieving %s address", sym); + + proc = yagl_egl_wgl_extaddr_get(dyn_lib, sym); + + if (!proc) { + YAGL_LOG_TRACE("wglGetProcAddress failed for %s, trying GetProcAddress", + sym); + proc = yagl_dyn_lib_get_sym(dyn_lib, sym); + } + + YAGL_LOG_FUNC_EXIT("%s address: %p", sym, proc); - YAGL_LOG_FUNC_EXIT("%p", proc); return proc; } -- 2.34.1