| /* |
| * Mac driver OpenGL support |
| * |
| * Copyright 2012 Alexandre Julliard |
| * Copyright 2012, 2013 Ken Thomases for CodeWeavers Inc. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include "macdrv.h" |
| |
| #include "winuser.h" |
| #include "winternl.h" |
| #include "winnt.h" |
| #include "wine/library.h" |
| #include "wine/debug.h" |
| #include "wine/wgl.h" |
| #include "wine/wgl_driver.h" |
| #include "wine/wglext.h" |
| |
| #define __gl_h_ |
| #define __gltypes_h_ |
| #include <OpenGL/OpenGL.h> |
| #include <OpenGL/glu.h> |
| #include <OpenGL/CGLRenderers.h> |
| #include <dlfcn.h> |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(wgl); |
| |
| |
| struct gl_info { |
| char *glExtensions; |
| |
| char wglExtensions[4096]; |
| |
| GLint max_viewport_dims[2]; |
| |
| unsigned int max_major, max_minor; |
| }; |
| |
| static struct gl_info gl_info; |
| |
| |
| struct wgl_context |
| { |
| struct list entry; |
| int format; |
| macdrv_opengl_context context; |
| CGLContextObj cglcontext; |
| HWND draw_hwnd; |
| macdrv_view draw_view; |
| struct wgl_pbuffer *draw_pbuffer; |
| macdrv_view read_view; |
| struct wgl_pbuffer *read_pbuffer; |
| BOOL has_been_current; |
| BOOL sharing; |
| LONG update_swap_interval; |
| DWORD last_flush_time; |
| UINT major; |
| }; |
| |
| static struct list context_list = LIST_INIT(context_list); |
| |
| static CRITICAL_SECTION context_section; |
| static CRITICAL_SECTION_DEBUG critsect_debug = |
| { |
| 0, 0, &context_section, |
| { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, |
| 0, 0, { (DWORD_PTR)(__FILE__ ": context_section") } |
| }; |
| static CRITICAL_SECTION context_section = { &critsect_debug, -1, 0, 0, 0, 0 }; |
| |
| |
| struct wgl_pbuffer |
| { |
| CGLPBufferObj pbuffer; |
| int format; |
| BOOL no_texture; |
| int max_level; |
| GLint level; |
| GLenum face; |
| }; |
| |
| static CFMutableDictionaryRef dc_pbuffers; |
| |
| static CRITICAL_SECTION dc_pbuffers_section; |
| static CRITICAL_SECTION_DEBUG dc_pbuffers_section_debug = |
| { |
| 0, 0, &dc_pbuffers_section, |
| { &dc_pbuffers_section_debug.ProcessLocksList, &dc_pbuffers_section_debug.ProcessLocksList }, |
| 0, 0, { (DWORD_PTR)(__FILE__ ": dc_pbuffers_section") } |
| }; |
| static CRITICAL_SECTION dc_pbuffers_section = { &dc_pbuffers_section_debug, -1, 0, 0, 0, 0 }; |
| |
| |
| static struct opengl_funcs opengl_funcs; |
| |
| #define USE_GL_FUNC(name) #name, |
| static const char *opengl_func_names[] = { ALL_WGL_FUNCS }; |
| #undef USE_GL_FUNC |
| |
| |
| static void (*pglCopyColorTable)(GLenum target, GLenum internalformat, GLint x, GLint y, |
| GLsizei width); |
| static void (*pglCopyPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); |
| static void (*pglFinish)(void); |
| static void (*pglFlush)(void); |
| static void (*pglFlushRenderAPPLE)(void); |
| static const GLubyte *(*pglGetString)(GLenum name); |
| static void (*pglReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, |
| GLenum format, GLenum type, void *pixels); |
| static void (*pglViewport)(GLint x, GLint y, GLsizei width, GLsizei height); |
| |
| |
| struct color_mode { |
| GLint mode; |
| int bits_per_pixel; |
| GLint color_bits; /* including alpha_bits */ |
| int red_bits, red_shift; |
| int green_bits, green_shift; |
| int blue_bits, blue_shift; |
| GLint alpha_bits, alpha_shift; |
| BOOL is_float; |
| int color_ordering; |
| }; |
| |
| /* The value of "color_ordering" is somewhat arbitrary. It incorporates some |
| observations of the behavior of Windows systems, but also subjective judgments |
| about what color formats are more "normal" than others. |
| |
| On at least some Windows systems, integer color formats are listed before |
| floating-point formats. Within the integer formats, higher color bits were |
| usually listed before lower color bits, while for floating-point formats it |
| was the reverse. However, that leads D3D to select 64-bit integer formats in |
| preference to 32-bit formats when the latter would be sufficient. It seems |
| that a 32-bit format is much more likely to be normally used in that case. |
| |
| Also, there are certain odd color formats supported on the Mac which seem like |
| they would be less appropriate than more common ones. For instance, the color |
| formats with alpha in a separate byte (e.g. kCGLRGB888A8Bit with R8G8B8 in one |
| 32-bit value and A8 in a separate 8-bit value) and the formats with 10-bit RGB |
| components. |
| |
| For two color formats which differ only in whether or not they have alpha bits, |
| we use the same ordering. pixel_format_comparator() gives alpha bits a |
| different weight than color formats. |
| */ |
| static const struct color_mode color_modes[] = { |
| { kCGLRGB444Bit, 16, 12, 4, 8, 4, 4, 4, 0, 0, 0, FALSE, 5 }, |
| { kCGLARGB4444Bit, 16, 16, 4, 8, 4, 4, 4, 0, 4, 12, FALSE, 5 }, |
| { kCGLRGB444A8Bit, 24, 20, 4, 8, 4, 4, 4, 0, 8, 16, FALSE, 10 }, |
| { kCGLRGB555Bit, 16, 15, 5, 10, 5, 5, 5, 0, 0, 0, FALSE, 4 }, |
| { kCGLARGB1555Bit, 16, 16, 5, 10, 5, 5, 5, 0, 1, 15, FALSE, 4 }, |
| { kCGLRGB555A8Bit, 24, 23, 5, 10, 5, 5, 5, 0, 8, 16, FALSE, 9 }, |
| { kCGLRGB565Bit, 16, 16, 5, 11, 6, 5, 5, 0, 0, 0, FALSE, 3 }, |
| { kCGLRGB565A8Bit, 24, 24, 5, 11, 6, 5, 5, 0, 8, 16, FALSE, 8 }, |
| { kCGLRGB888Bit, 32, 24, 8, 16, 8, 8, 8, 0, 0, 0, FALSE, 0 }, |
| { kCGLARGB8888Bit, 32, 32, 8, 16, 8, 8, 8, 0, 8, 24, FALSE, 0 }, |
| { kCGLRGB888A8Bit, 40, 32, 8, 16, 8, 8, 8, 0, 8, 32, FALSE, 7 }, |
| { kCGLRGB101010Bit, 32, 30, 10, 20, 10, 10, 10, 0, 0, 0, FALSE, 6 }, |
| { kCGLARGB2101010Bit, 32, 32, 10, 20, 10, 10, 10, 0, 2, 30, FALSE, 6 }, |
| { kCGLRGB101010_A8Bit, 40, 38, 10, 20, 10, 10, 10, 0, 8, 32, FALSE, 11 }, |
| { kCGLRGB121212Bit, 48, 36, 12, 24, 12, 12, 12, 0, 0, 0, FALSE, 2 }, |
| { kCGLARGB12121212Bit, 48, 48, 12, 24, 12, 12, 12, 0, 12, 36, FALSE, 2 }, |
| { kCGLRGB161616Bit, 64, 48, 16, 48, 16, 32, 16, 16, 0, 0, FALSE, 1 }, |
| { kCGLRGBA16161616Bit, 64, 64, 16, 48, 16, 32, 16, 16, 16, 0, FALSE, 1 }, |
| { kCGLRGBFloat64Bit, 64, 48, 16, 32, 16, 16, 16, 0, 0, 0, TRUE, 12 }, |
| { kCGLRGBAFloat64Bit, 64, 64, 16, 48, 16, 32, 16, 16, 16, 0, TRUE, 12 }, |
| { kCGLRGBFloat128Bit, 128, 96, 32, 96, 32, 64, 32, 32, 0, 0, TRUE, 13 }, |
| { kCGLRGBAFloat128Bit, 128, 128, 32, 96, 32, 64, 32, 32, 32, 0, TRUE, 13 }, |
| { kCGLRGBFloat256Bit, 256, 192, 64, 192, 64, 128, 64, 64, 0, 0, TRUE, 14 }, |
| { kCGLRGBAFloat256Bit, 256, 256, 64, 192, 64, 128, 64, 64, 64, 0, TRUE, 15 }, |
| }; |
| |
| |
| static const struct { |
| GLint mode; |
| int bits; |
| } depth_stencil_modes[] = { |
| { kCGL0Bit, 0 }, |
| { kCGL1Bit, 1 }, |
| { kCGL2Bit, 2 }, |
| { kCGL3Bit, 3 }, |
| { kCGL4Bit, 4 }, |
| { kCGL5Bit, 5 }, |
| { kCGL6Bit, 6 }, |
| { kCGL8Bit, 8 }, |
| { kCGL10Bit, 10 }, |
| { kCGL12Bit, 12 }, |
| { kCGL16Bit, 16 }, |
| { kCGL24Bit, 24 }, |
| { kCGL32Bit, 32 }, |
| { kCGL48Bit, 48 }, |
| { kCGL64Bit, 64 }, |
| { kCGL96Bit, 96 }, |
| { kCGL128Bit, 128 }, |
| }; |
| |
| |
| typedef struct { |
| GLint renderer_id; |
| GLint buffer_modes; |
| GLint color_modes; |
| GLint accum_modes; |
| GLint depth_modes; |
| GLint stencil_modes; |
| GLint max_aux_buffers; |
| GLint max_sample_buffers; |
| GLint max_samples; |
| BOOL offscreen; |
| BOOL accelerated; |
| BOOL backing_store; |
| BOOL window; |
| BOOL online; |
| } renderer_properties; |
| |
| |
| typedef struct { |
| unsigned int window:1; |
| unsigned int pbuffer:1; |
| unsigned int accelerated:1; |
| unsigned int color_mode:5; /* index into color_modes table */ |
| unsigned int aux_buffers:3; |
| unsigned int depth_bits:8; |
| unsigned int stencil_bits:8; |
| unsigned int accum_mode:5; /* 1 + index into color_modes table (0 means no accum buffer) */ |
| unsigned int double_buffer:1; |
| unsigned int stereo:1; |
| unsigned int sample_buffers:1; |
| unsigned int samples:5; |
| unsigned int backing_store:1; |
| } pixel_format; |
| |
| |
| typedef union |
| { |
| pixel_format format; |
| UInt64 code; |
| } pixel_format_or_code; |
| C_ASSERT(sizeof(((pixel_format_or_code*)0)->format) <= sizeof(((pixel_format_or_code*)0)->code)); |
| |
| |
| static pixel_format *pixel_formats; |
| static int nb_formats, nb_displayable_formats; |
| |
| |
| static void *opengl_handle; |
| |
| |
| static const char* debugstr_attrib(int attrib, int value) |
| { |
| static const struct { |
| int attrib; |
| const char *name; |
| } attrib_names[] = { |
| #define ATTRIB(a) { a, #a } |
| ATTRIB(WGL_ACCELERATION_ARB), |
| ATTRIB(WGL_ACCUM_ALPHA_BITS_ARB), |
| ATTRIB(WGL_ACCUM_BITS_ARB), |
| ATTRIB(WGL_ACCUM_BLUE_BITS_ARB), |
| ATTRIB(WGL_ACCUM_GREEN_BITS_ARB), |
| ATTRIB(WGL_ACCUM_RED_BITS_ARB), |
| ATTRIB(WGL_ALPHA_BITS_ARB), |
| ATTRIB(WGL_ALPHA_SHIFT_ARB), |
| ATTRIB(WGL_AUX_BUFFERS_ARB), |
| ATTRIB(WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV), |
| ATTRIB(WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV), |
| ATTRIB(WGL_BIND_TO_TEXTURE_RGB_ARB), |
| ATTRIB(WGL_BIND_TO_TEXTURE_RGBA_ARB), |
| ATTRIB(WGL_BLUE_BITS_ARB), |
| ATTRIB(WGL_BLUE_SHIFT_ARB), |
| ATTRIB(WGL_COLOR_BITS_ARB), |
| ATTRIB(WGL_DEPTH_BITS_ARB), |
| ATTRIB(WGL_DOUBLE_BUFFER_ARB), |
| ATTRIB(WGL_DRAW_TO_BITMAP_ARB), |
| ATTRIB(WGL_DRAW_TO_PBUFFER_ARB), |
| ATTRIB(WGL_DRAW_TO_WINDOW_ARB), |
| ATTRIB(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB), |
| ATTRIB(WGL_GREEN_BITS_ARB), |
| ATTRIB(WGL_GREEN_SHIFT_ARB), |
| ATTRIB(WGL_NEED_PALETTE_ARB), |
| ATTRIB(WGL_NEED_SYSTEM_PALETTE_ARB), |
| ATTRIB(WGL_NUMBER_OVERLAYS_ARB), |
| ATTRIB(WGL_NUMBER_PIXEL_FORMATS_ARB), |
| ATTRIB(WGL_NUMBER_UNDERLAYS_ARB), |
| ATTRIB(WGL_PIXEL_TYPE_ARB), |
| ATTRIB(WGL_RED_BITS_ARB), |
| ATTRIB(WGL_RED_SHIFT_ARB), |
| ATTRIB(WGL_SAMPLE_BUFFERS_ARB), |
| ATTRIB(WGL_SAMPLES_ARB), |
| ATTRIB(WGL_SHARE_ACCUM_ARB), |
| ATTRIB(WGL_SHARE_DEPTH_ARB), |
| ATTRIB(WGL_SHARE_STENCIL_ARB), |
| ATTRIB(WGL_STENCIL_BITS_ARB), |
| ATTRIB(WGL_STEREO_ARB), |
| ATTRIB(WGL_SUPPORT_GDI_ARB), |
| ATTRIB(WGL_SUPPORT_OPENGL_ARB), |
| ATTRIB(WGL_SWAP_LAYER_BUFFERS_ARB), |
| ATTRIB(WGL_SWAP_METHOD_ARB), |
| ATTRIB(WGL_TRANSPARENT_ALPHA_VALUE_ARB), |
| ATTRIB(WGL_TRANSPARENT_ARB), |
| ATTRIB(WGL_TRANSPARENT_BLUE_VALUE_ARB), |
| ATTRIB(WGL_TRANSPARENT_GREEN_VALUE_ARB), |
| ATTRIB(WGL_TRANSPARENT_INDEX_VALUE_ARB), |
| ATTRIB(WGL_TRANSPARENT_RED_VALUE_ARB), |
| #undef ATTRIB |
| }; |
| int i; |
| const char *attrib_name = NULL; |
| const char *value_name = NULL; |
| |
| for (i = 0; i < sizeof(attrib_names) / sizeof(attrib_names[0]); i++) |
| { |
| if (attrib_names[i].attrib == attrib) |
| { |
| attrib_name = attrib_names[i].name; |
| break; |
| } |
| } |
| |
| if (!attrib_name) |
| attrib_name = wine_dbg_sprintf("Attrib 0x%04x", attrib); |
| |
| switch (attrib) |
| { |
| case WGL_ACCELERATION_ARB: |
| switch (value) |
| { |
| case WGL_FULL_ACCELERATION_ARB: value_name = "WGL_FULL_ACCELERATION_ARB"; break; |
| case WGL_GENERIC_ACCELERATION_ARB: value_name = "WGL_GENERIC_ACCELERATION_ARB"; break; |
| case WGL_NO_ACCELERATION_ARB: value_name = "WGL_NO_ACCELERATION_ARB"; break; |
| } |
| break; |
| case WGL_PIXEL_TYPE_ARB: |
| switch (value) |
| { |
| case WGL_TYPE_COLORINDEX_ARB: value_name = "WGL_TYPE_COLORINDEX_ARB"; break; |
| case WGL_TYPE_RGBA_ARB: value_name = "WGL_TYPE_RGBA_ARB"; break; |
| case WGL_TYPE_RGBA_FLOAT_ARB: value_name = "WGL_TYPE_RGBA_FLOAT_ARB"; break; |
| case WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT: value_name = "WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT"; break; |
| } |
| break; |
| case WGL_SWAP_METHOD_ARB: |
| switch (value) |
| { |
| case WGL_SWAP_COPY_ARB: value_name = "WGL_SWAP_COPY_ARB"; break; |
| case WGL_SWAP_EXCHANGE_ARB: value_name = "WGL_SWAP_EXCHANGE_ARB"; break; |
| case WGL_SWAP_UNDEFINED_ARB: value_name = "WGL_SWAP_UNDEFINED_ARB"; break; |
| } |
| break; |
| } |
| |
| if (!value_name) |
| value_name = wine_dbg_sprintf("%d / 0x%04x", value, value); |
| |
| return wine_dbg_sprintf("%40s: %s", attrib_name, value_name); |
| } |
| |
| |
| static BOOL get_renderer_property(CGLRendererInfoObj renderer_info, GLint renderer_index, |
| CGLRendererProperty property, GLint *value) |
| { |
| CGLError err = CGLDescribeRenderer(renderer_info, renderer_index, property, value); |
| if (err != kCGLNoError) |
| WARN("CGLDescribeRenderer failed for property %d: %d %s\n", property, err, CGLErrorString(err)); |
| return (err == kCGLNoError); |
| } |
| |
| |
| static void get_renderer_properties(CGLRendererInfoObj renderer_info, int renderer_index, renderer_properties* properties) |
| { |
| GLint value; |
| |
| memset(properties, 0, sizeof(*properties)); |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPRendererID, &value)) |
| properties->renderer_id = value & kCGLRendererIDMatchingMask; |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPBufferModes, &value)) |
| properties->buffer_modes = value; |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPColorModes, &value)) |
| properties->color_modes = value; |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPAccumModes, &value)) |
| properties->accum_modes = value; |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPDepthModes, &value)) |
| properties->depth_modes = value; |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPStencilModes, &value)) |
| properties->stencil_modes = value; |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPMaxAuxBuffers, &value)) |
| properties->max_aux_buffers = value; |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPMaxSampleBuffers, &value)) |
| properties->max_sample_buffers = value; |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPMaxSamples, &value)) |
| properties->max_samples = value; |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPOffScreen, &value)) |
| properties->offscreen = (value != 0); |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPAccelerated, &value)) |
| properties->accelerated = (value != 0); |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPBackingStore, &value)) |
| properties->backing_store = (value != 0); |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPWindow, &value)) |
| properties->window = (value != 0); |
| |
| if (get_renderer_property(renderer_info, renderer_index, kCGLRPOnline, &value)) |
| properties->online = (value != 0); |
| } |
| |
| |
| static void dump_renderer(const renderer_properties* renderer) |
| { |
| int i; |
| |
| TRACE("Renderer ID: 0x%08x\n", renderer->renderer_id); |
| TRACE("Buffer modes:\n"); |
| TRACE(" Monoscopic: %s\n", (renderer->buffer_modes & kCGLMonoscopicBit) ? "YES" : "NO"); |
| TRACE(" Stereoscopic: %s\n", (renderer->buffer_modes & kCGLStereoscopicBit) ? "YES" : "NO"); |
| TRACE(" Single buffer: %s\n", (renderer->buffer_modes & kCGLSingleBufferBit) ? "YES" : "NO"); |
| TRACE(" Double buffer: %s\n", (renderer->buffer_modes & kCGLDoubleBufferBit) ? "YES" : "NO"); |
| |
| TRACE("Color buffer modes:\n"); |
| for (i = 0; i < sizeof(color_modes)/sizeof(color_modes[0]); i++) |
| { |
| if (renderer->color_modes & color_modes[i].mode) |
| { |
| TRACE(" Color size %d, Alpha size %d", color_modes[i].color_bits, color_modes[i].alpha_bits); |
| if (color_modes[i].is_float) |
| TRACE(", Float"); |
| TRACE("\n"); |
| } |
| } |
| |
| TRACE("Accumulation buffer sizes: { "); |
| for (i = 0; i < sizeof(color_modes)/sizeof(color_modes[0]); i++) |
| { |
| if (renderer->accum_modes & color_modes[i].mode) |
| TRACE("%d, ", color_modes[i].color_bits); |
| } |
| TRACE("}\n"); |
| |
| TRACE("Depth buffer sizes: { "); |
| for (i = 0; i < sizeof(depth_stencil_modes)/sizeof(depth_stencil_modes[0]); i++) |
| { |
| if (renderer->depth_modes & depth_stencil_modes[i].mode) |
| TRACE("%d, ", depth_stencil_modes[i].bits); |
| } |
| TRACE("}\n"); |
| |
| TRACE("Stencil buffer sizes: { "); |
| for (i = 0; i < sizeof(depth_stencil_modes)/sizeof(depth_stencil_modes[0]); i++) |
| { |
| if (renderer->stencil_modes & depth_stencil_modes[i].mode) |
| TRACE("%d, ", depth_stencil_modes[i].bits); |
| } |
| TRACE("}\n"); |
| |
| TRACE("Max. Auxiliary Buffers: %d\n", renderer->max_aux_buffers); |
| TRACE("Max. Sample Buffers: %d\n", renderer->max_sample_buffers); |
| TRACE("Max. Samples: %d\n", renderer->max_samples); |
| TRACE("Offscreen: %s\n", renderer->offscreen ? "YES" : "NO"); |
| TRACE("Accelerated: %s\n", renderer->accelerated ? "YES" : "NO"); |
| TRACE("Backing store: %s\n", renderer->backing_store ? "YES" : "NO"); |
| TRACE("Window: %s\n", renderer->window ? "YES" : "NO"); |
| TRACE("Online: %s\n", renderer->online ? "YES" : "NO"); |
| } |
| |
| |
| static inline UInt64 code_for_pixel_format(const pixel_format* format) |
| { |
| pixel_format_or_code pfc; |
| |
| pfc.code = 0; |
| pfc.format = *format; |
| return pfc.code; |
| } |
| |
| |
| static inline pixel_format pixel_format_for_code(UInt64 code) |
| { |
| pixel_format_or_code pfc; |
| |
| pfc.code = code; |
| return pfc.format; |
| } |
| |
| |
| static const char *debugstr_pf(const pixel_format *pf) |
| { |
| return wine_dbg_sprintf("w/p/a %u/%u/%u col %u%s/%u dp/stn/ac/ax/b/db/str %u/%u/%u/%u/%u/%u/%u samp %u/%u %017llx", |
| pf->window, |
| pf->pbuffer, |
| pf->accelerated, |
| color_modes[pf->color_mode].color_bits, |
| (color_modes[pf->color_mode].is_float ? "f" : ""), |
| color_modes[pf->color_mode].alpha_bits, |
| pf->depth_bits, |
| pf->stencil_bits, |
| pf->accum_mode ? color_modes[pf->accum_mode - 1].color_bits : 0, |
| pf->aux_buffers, |
| pf->backing_store, |
| pf->double_buffer, |
| pf->stereo, |
| pf->sample_buffers, |
| pf->samples, |
| code_for_pixel_format(pf)); |
| } |
| |
| |
| static unsigned int best_color_mode(GLint modes, GLint color_size, GLint alpha_size, GLint color_float) |
| { |
| int best = -1; |
| int i; |
| |
| for (i = 0; i < sizeof(color_modes)/sizeof(color_modes[0]); i++) |
| { |
| if ((modes & color_modes[i].mode) && |
| color_modes[i].color_bits >= color_size && |
| color_modes[i].alpha_bits >= alpha_size && |
| !color_modes[i].is_float == !color_float) |
| { |
| if (best < 0) /* no existing best choice */ |
| best = i; |
| else if (color_modes[i].color_bits == color_size && |
| color_modes[i].alpha_bits == alpha_size) /* candidate is exact match */ |
| { |
| /* prefer it over a best which isn't exact or which has a higher bpp */ |
| if (color_modes[best].color_bits != color_size || |
| color_modes[best].alpha_bits != alpha_size || |
| color_modes[i].bits_per_pixel < color_modes[best].bits_per_pixel) |
| best = i; |
| } |
| else if (color_modes[i].color_bits < color_modes[best].color_bits || |
| (color_modes[i].color_bits == color_modes[best].color_bits && |
| color_modes[i].alpha_bits < color_modes[best].alpha_bits)) /* prefer closer */ |
| best = i; |
| } |
| } |
| |
| if (best < 0) |
| { |
| /* Couldn't find a match. Return first one that renderer supports. */ |
| for (i = 0; i < sizeof(color_modes)/sizeof(color_modes[0]); i++) |
| { |
| if (modes & color_modes[i].mode) |
| return i; |
| } |
| } |
| |
| return best; |
| } |
| |
| |
| static unsigned int best_accum_mode(GLint modes, GLint accum_size) |
| { |
| int best = -1; |
| int i; |
| |
| for (i = 0; i < sizeof(color_modes)/sizeof(color_modes[0]); i++) |
| { |
| if ((modes & color_modes[i].mode) && color_modes[i].color_bits >= accum_size) |
| { |
| /* Prefer the fewest color bits, then prefer more alpha bits, then |
| prefer more bits per pixel. */ |
| if (best < 0) |
| best = i; |
| else if (color_modes[i].color_bits < color_modes[best].color_bits) |
| best = i; |
| else if (color_modes[i].color_bits == color_modes[best].color_bits) |
| { |
| if (color_modes[i].alpha_bits > color_modes[best].alpha_bits) |
| best = i; |
| else if (color_modes[i].alpha_bits == color_modes[best].alpha_bits && |
| color_modes[i].bits_per_pixel > color_modes[best].bits_per_pixel) |
| best = i; |
| } |
| } |
| } |
| |
| if (best < 0) |
| { |
| /* Couldn't find a match. Return last one that renderer supports. */ |
| for (i = sizeof(color_modes)/sizeof(color_modes[0]) - 1; i >= 0; i--) |
| { |
| if (modes & color_modes[i].mode) |
| return i; |
| } |
| } |
| |
| return best; |
| } |
| |
| |
| static void enum_renderer_pixel_formats(renderer_properties renderer, CFMutableArrayRef pixel_format_array, |
| CFMutableSetRef pixel_format_set) |
| { |
| CGLPixelFormatAttribute attribs[64] = { |
| kCGLPFAMinimumPolicy, |
| kCGLPFAClosestPolicy, |
| kCGLPFARendererID, renderer.renderer_id, |
| kCGLPFASingleRenderer, |
| }; |
| int n = 5, n_stack[16], n_stack_idx = -1; |
| unsigned int tried_pixel_formats = 0, failed_pixel_formats = 0, dupe_pixel_formats = 0, |
| new_pixel_formats = 0; |
| pixel_format request; |
| unsigned int double_buffer; |
| unsigned int accelerated = renderer.accelerated; |
| |
| if (accelerated) |
| { |
| attribs[n++] = kCGLPFAAccelerated; |
| attribs[n++] = kCGLPFANoRecovery; |
| } |
| else if (!allow_software_rendering) |
| { |
| TRACE("ignoring software renderer because AllowSoftwareRendering is off\n"); |
| return; |
| } |
| |
| n_stack[++n_stack_idx] = n; |
| for (double_buffer = 0; double_buffer <= 1; double_buffer++) |
| { |
| unsigned int aux; |
| |
| n = n_stack[n_stack_idx]; |
| |
| if ((!double_buffer && !(renderer.buffer_modes & kCGLSingleBufferBit)) || |
| (double_buffer && !(renderer.buffer_modes & kCGLDoubleBufferBit))) |
| continue; |
| |
| if (double_buffer) |
| attribs[n++] = kCGLPFADoubleBuffer; |
| memset(&request, 0, sizeof(request)); |
| request.accelerated = accelerated; |
| request.double_buffer = double_buffer; |
| |
| /* Don't bother with in-between aux buffers values: either 0 or max. */ |
| n_stack[++n_stack_idx] = n; |
| for (aux = 0; aux <= renderer.max_aux_buffers; aux += renderer.max_aux_buffers) |
| { |
| unsigned int color_mode; |
| |
| n = n_stack[n_stack_idx]; |
| |
| attribs[n++] = kCGLPFAAuxBuffers; |
| attribs[n++] = aux; |
| request.aux_buffers = aux; |
| |
| n_stack[++n_stack_idx] = n; |
| for (color_mode = 0; color_mode < sizeof(color_modes)/sizeof(color_modes[0]); color_mode++) |
| { |
| unsigned int depth_mode; |
| |
| n = n_stack[n_stack_idx]; |
| |
| if (!(renderer.color_modes & color_modes[color_mode].mode)) |
| continue; |
| |
| attribs[n++] = kCGLPFAColorSize; |
| attribs[n++] = color_modes[color_mode].color_bits; |
| attribs[n++] = kCGLPFAAlphaSize; |
| attribs[n++] = color_modes[color_mode].alpha_bits; |
| if (color_modes[color_mode].is_float) |
| attribs[n++] = kCGLPFAColorFloat; |
| request.color_mode = color_mode; |
| |
| n_stack[++n_stack_idx] = n; |
| for (depth_mode = 0; depth_mode < sizeof(depth_stencil_modes)/sizeof(depth_stencil_modes[0]); depth_mode++) |
| { |
| unsigned int stencil_mode; |
| |
| n = n_stack[n_stack_idx]; |
| |
| if (!(renderer.depth_modes & depth_stencil_modes[depth_mode].mode)) |
| continue; |
| |
| attribs[n++] = kCGLPFADepthSize; |
| attribs[n++] = depth_stencil_modes[depth_mode].bits; |
| request.depth_bits = depth_stencil_modes[depth_mode].bits; |
| |
| n_stack[++n_stack_idx] = n; |
| for (stencil_mode = 0; stencil_mode < sizeof(depth_stencil_modes)/sizeof(depth_stencil_modes[0]); stencil_mode++) |
| { |
| unsigned int stereo; |
| |
| n = n_stack[n_stack_idx]; |
| |
| if (!(renderer.stencil_modes & depth_stencil_modes[stencil_mode].mode)) |
| continue; |
| if (accelerated && depth_stencil_modes[depth_mode].bits != 24 && stencil_mode > 0) |
| continue; |
| |
| attribs[n++] = kCGLPFAStencilSize; |
| attribs[n++] = depth_stencil_modes[stencil_mode].bits; |
| request.stencil_bits = depth_stencil_modes[stencil_mode].bits; |
| |
| /* FIXME: Could trim search space a bit here depending on GPU. |
| For ATI Radeon HD 4850, kCGLRGBA16161616Bit implies stereo-capable. */ |
| n_stack[++n_stack_idx] = n; |
| for (stereo = 0; stereo <= 1; stereo++) |
| { |
| int accum_mode; |
| |
| n = n_stack[n_stack_idx]; |
| |
| if ((!stereo && !(renderer.buffer_modes & kCGLMonoscopicBit)) || |
| (stereo && !(renderer.buffer_modes & kCGLStereoscopicBit))) |
| continue; |
| |
| if (stereo) |
| attribs[n++] = kCGLPFAStereo; |
| request.stereo = stereo; |
| |
| /* Starts at -1 for a 0 accum size */ |
| n_stack[++n_stack_idx] = n; |
| for (accum_mode = -1; accum_mode < (int)(sizeof(color_modes)/sizeof(color_modes[0])); accum_mode++) |
| { |
| unsigned int target_pass; |
| |
| n = n_stack[n_stack_idx]; |
| |
| if (accum_mode >= 0) |
| { |
| if (!(renderer.accum_modes & color_modes[accum_mode].mode)) |
| continue; |
| |
| attribs[n++] = kCGLPFAAccumSize; |
| attribs[n++] = color_modes[accum_mode].color_bits; |
| request.accum_mode = accum_mode + 1; |
| } |
| else |
| request.accum_mode = 0; |
| |
| /* Targets to request are: |
| accelerated: window OR window + pbuffer |
| software: window + pbuffer */ |
| n_stack[++n_stack_idx] = n; |
| for (target_pass = 0; target_pass <= accelerated; target_pass++) |
| { |
| unsigned int samples, max_samples; |
| |
| n = n_stack[n_stack_idx]; |
| |
| attribs[n++] = kCGLPFAWindow; |
| request.window = 1; |
| |
| if (!accelerated || target_pass > 0) |
| { |
| attribs[n++] = kCGLPFAPBuffer; |
| request.pbuffer = 1; |
| } |
| else |
| request.pbuffer = 0; |
| |
| /* FIXME: Could trim search space a bit here depending on GPU. |
| For Nvidia GeForce 8800 GT, limited to 4 samples for color_bits >= 128. |
| For ATI Radeon HD 4850, can't multi-sample for color_bits >= 64 or pbuffer. */ |
| n_stack[++n_stack_idx] = n; |
| max_samples = renderer.max_sample_buffers ? max(1, renderer.max_samples) : 1; |
| for (samples = 1; samples <= max_samples; samples *= 2) |
| { |
| unsigned int backing_store, min_backing_store, max_backing_store; |
| |
| n = n_stack[n_stack_idx]; |
| |
| if (samples > 1) |
| { |
| attribs[n++] = kCGLPFASampleBuffers; |
| attribs[n++] = renderer.max_sample_buffers; |
| attribs[n++] = kCGLPFASamples; |
| attribs[n++] = samples; |
| request.sample_buffers = renderer.max_sample_buffers; |
| request.samples = samples; |
| } |
| else |
| request.sample_buffers = request.samples = 0; |
| |
| if (renderer.backing_store && double_buffer) |
| { |
| /* The software renderer seems to always preserve the backing store, whether |
| we ask for it or not. So don't bother not asking for it. */ |
| min_backing_store = accelerated ? 0 : 1; |
| max_backing_store = 1; |
| } |
| else |
| min_backing_store = max_backing_store = 0; |
| n_stack[++n_stack_idx] = n; |
| for (backing_store = min_backing_store; backing_store <= max_backing_store; backing_store++) |
| { |
| CGLPixelFormatObj pix; |
| GLint virtualScreens; |
| CGLError err; |
| |
| n = n_stack[n_stack_idx]; |
| |
| if (backing_store) |
| attribs[n++] = kCGLPFABackingStore; |
| request.backing_store = backing_store; |
| |
| attribs[n] = 0; |
| |
| err = CGLChoosePixelFormat(attribs, &pix, &virtualScreens); |
| if (err == kCGLNoError && pix) |
| { |
| pixel_format pf; |
| GLint value, color_size, alpha_size, color_float; |
| UInt64 pf_code; |
| CFNumberRef code_object; |
| BOOL dupe; |
| |
| memset(&pf, 0, sizeof(pf)); |
| |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFAAccelerated, &value) == kCGLNoError) |
| pf.accelerated = value; |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFAAuxBuffers, &value) == kCGLNoError) |
| pf.aux_buffers = value; |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFADepthSize, &value) == kCGLNoError) |
| pf.depth_bits = value; |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFADoubleBuffer, &value) == kCGLNoError) |
| pf.double_buffer = value; |
| if (pf.double_buffer && |
| CGLDescribePixelFormat(pix, 0, kCGLPFABackingStore, &value) == kCGLNoError) |
| pf.backing_store = value; |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFAPBuffer, &value) == kCGLNoError) |
| pf.pbuffer = value; |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFASampleBuffers, &value) == kCGLNoError) |
| pf.sample_buffers = value; |
| if (pf.sample_buffers && |
| CGLDescribePixelFormat(pix, 0, kCGLPFASamples, &value) == kCGLNoError) |
| pf.samples = value; |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFAStencilSize, &value) == kCGLNoError) |
| pf.stencil_bits = value; |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFAStereo, &value) == kCGLNoError) |
| pf.stereo = value; |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFAWindow, &value) == kCGLNoError) |
| pf.window = value; |
| |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFAColorSize, &color_size) != kCGLNoError) |
| color_size = 0; |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFAAlphaSize, &alpha_size) != kCGLNoError) |
| alpha_size = 0; |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFAColorFloat, &color_float) != kCGLNoError) |
| color_float = 0; |
| pf.color_mode = best_color_mode(renderer.color_modes, color_size, alpha_size, color_float); |
| |
| if (CGLDescribePixelFormat(pix, 0, kCGLPFAAccumSize, &value) == kCGLNoError && value) |
| pf.accum_mode = best_accum_mode(renderer.accum_modes, value) + 1; |
| |
| CGLReleasePixelFormat(pix); |
| |
| pf_code = code_for_pixel_format(&pf); |
| |
| code_object = CFNumberCreate(NULL, kCFNumberSInt64Type, &pf_code); |
| if ((dupe = CFSetContainsValue(pixel_format_set, code_object))) |
| dupe_pixel_formats++; |
| else |
| { |
| CFSetAddValue(pixel_format_set, code_object); |
| CFArrayAppendValue(pixel_format_array, code_object); |
| new_pixel_formats++; |
| } |
| CFRelease(code_object); |
| |
| if (pf_code == code_for_pixel_format(&request)) |
| TRACE("%s%s\n", debugstr_pf(&pf), dupe ? " (duplicate)" : ""); |
| else |
| { |
| TRACE("%s remapped from %s%s\n", debugstr_pf(&pf), debugstr_pf(&request), |
| dupe ? " (duplicate)" : ""); |
| } |
| } |
| else |
| { |
| failed_pixel_formats++; |
| TRACE("%s failed request err %d %s\n", debugstr_pf(&request), err, err ? CGLErrorString(err) : ""); |
| } |
| |
| tried_pixel_formats++; |
| } |
| |
| n_stack_idx--; |
| } |
| |
| n_stack_idx--; |
| } |
| |
| n_stack_idx--; |
| } |
| |
| n_stack_idx--; |
| } |
| |
| n_stack_idx--; |
| } |
| |
| n_stack_idx--; |
| } |
| |
| n_stack_idx--; |
| } |
| |
| n_stack_idx--; |
| } |
| |
| n_stack_idx--; |
| } |
| |
| n_stack_idx--; |
| |
| TRACE("Number of pixel format attribute combinations: %u\n", tried_pixel_formats); |
| TRACE(" Number which failed to choose a pixel format: %u\n", failed_pixel_formats); |
| TRACE(" Number which chose redundant pixel formats: %u\n", dupe_pixel_formats); |
| TRACE("Number of new pixel formats for this renderer: %u\n", new_pixel_formats); |
| } |
| |
| |
| /* The docs for WGL_ARB_pixel_format say: |
| Indices are assigned to pixel formats in the following order: |
| 1. Accelerated pixel formats that are displayable |
| 2. Accelerated pixel formats that are displayable and which have |
| extended attributes |
| 3. Generic pixel formats |
| 4. Accelerated pixel formats that are non displayable |
| */ |
| static int pixel_format_category(pixel_format pf) |
| { |
| /* non-displayable */ |
| if (!pf.window) |
| return 4; |
| |
| /* non-accelerated a.k.a. software a.k.a. generic */ |
| if (!pf.accelerated) |
| return 3; |
| |
| /* extended attributes that can't be represented in PIXELFORMATDESCRIPTOR */ |
| if (color_modes[pf.color_mode].is_float) |
| return 2; |
| |
| /* accelerated, displayable, no extended attributes */ |
| return 1; |
| } |
| |
| |
| static CFComparisonResult pixel_format_comparator(const void *val1, const void *val2, void *context) |
| { |
| CFNumberRef number1 = val1; |
| CFNumberRef number2 = val2; |
| UInt64 code1, code2; |
| pixel_format pf1, pf2; |
| int category1, category2; |
| |
| CFNumberGetValue(number1, kCFNumberLongLongType, &code1); |
| CFNumberGetValue(number2, kCFNumberLongLongType, &code2); |
| pf1 = pixel_format_for_code(code1); |
| pf2 = pixel_format_for_code(code2); |
| category1 = pixel_format_category(pf1); |
| category2 = pixel_format_category(pf2); |
| |
| if (category1 < category2) |
| return kCFCompareLessThan; |
| if (category1 > category2) |
| return kCFCompareGreaterThan; |
| |
| /* Within a category, sort the "best" formats toward the front since that's |
| what wglChoosePixelFormatARB() has to do. The ordering implemented here |
| matches at least one Windows 7 machine's behavior. |
| */ |
| /* Accelerated before unaccelerated. */ |
| if (pf1.accelerated && !pf2.accelerated) |
| return kCFCompareLessThan; |
| if (!pf1.accelerated && pf2.accelerated) |
| return kCFCompareGreaterThan; |
| |
| /* Explicit color mode ordering. */ |
| if (color_modes[pf1.color_mode].color_ordering < color_modes[pf2.color_mode].color_ordering) |
| return kCFCompareLessThan; |
| if (color_modes[pf1.color_mode].color_ordering > color_modes[pf2.color_mode].color_ordering) |
| return kCFCompareGreaterThan; |
| |
| /* Non-pbuffer-capable before pbuffer-capable. */ |
| if (!pf1.pbuffer && pf2.pbuffer) |
| return kCFCompareLessThan; |
| if (pf1.pbuffer && !pf2.pbuffer) |
| return kCFCompareGreaterThan; |
| |
| /* Fewer samples before more samples. */ |
| if (pf1.samples < pf2.samples) |
| return kCFCompareLessThan; |
| if (pf1.samples > pf2.samples) |
| return kCFCompareGreaterThan; |
| |
| /* Monoscopic before stereoscopic. (This is a guess.) */ |
| if (!pf1.stereo && pf2.stereo) |
| return kCFCompareLessThan; |
| if (pf1.stereo && !pf2.stereo) |
| return kCFCompareGreaterThan; |
| |
| /* Single buffered before double buffered. */ |
| if (!pf1.double_buffer && pf2.double_buffer) |
| return kCFCompareLessThan; |
| if (pf1.double_buffer && !pf2.double_buffer) |
| return kCFCompareGreaterThan; |
| |
| /* Possibly-optimized double buffering before backing-store-preserving |
| double buffering. */ |
| if (!pf1.backing_store && pf2.backing_store) |
| return kCFCompareLessThan; |
| if (pf1.backing_store && !pf2.backing_store) |
| return kCFCompareGreaterThan; |
| |
| /* Bigger depth buffer before smaller depth buffer. */ |
| if (pf1.depth_bits > pf2.depth_bits) |
| return kCFCompareLessThan; |
| if (pf1.depth_bits < pf2.depth_bits) |
| return kCFCompareGreaterThan; |
| |
| /* Smaller stencil buffer before bigger stencil buffer. */ |
| if (pf1.stencil_bits < pf2.stencil_bits) |
| return kCFCompareLessThan; |
| if (pf1.stencil_bits > pf2.stencil_bits) |
| return kCFCompareGreaterThan; |
| |
| /* Smaller alpha bits before larger alpha bits. */ |
| if (color_modes[pf1.color_mode].alpha_bits < color_modes[pf2.color_mode].alpha_bits) |
| return kCFCompareLessThan; |
| if (color_modes[pf1.color_mode].alpha_bits > color_modes[pf2.color_mode].alpha_bits) |
| return kCFCompareGreaterThan; |
| |
| /* Smaller accum buffer before larger accum buffer. (This is a guess.) */ |
| if (pf1.accum_mode) |
| { |
| if (pf2.accum_mode) |
| { |
| if (color_modes[pf1.accum_mode - 1].color_bits - color_modes[pf1.accum_mode - 1].alpha_bits < |
| color_modes[pf2.accum_mode - 1].color_bits - color_modes[pf2.accum_mode - 1].alpha_bits) |
| return kCFCompareLessThan; |
| if (color_modes[pf1.accum_mode - 1].color_bits - color_modes[pf1.accum_mode - 1].alpha_bits > |
| color_modes[pf2.accum_mode - 1].color_bits - color_modes[pf2.accum_mode - 1].alpha_bits) |
| return kCFCompareGreaterThan; |
| |
| if (color_modes[pf1.accum_mode - 1].bits_per_pixel < color_modes[pf2.accum_mode - 1].bits_per_pixel) |
| return kCFCompareLessThan; |
| if (color_modes[pf1.accum_mode - 1].bits_per_pixel > color_modes[pf2.accum_mode - 1].bits_per_pixel) |
| return kCFCompareGreaterThan; |
| |
| if (color_modes[pf1.accum_mode - 1].alpha_bits < color_modes[pf2.accum_mode - 1].alpha_bits) |
| return kCFCompareLessThan; |
| if (color_modes[pf1.accum_mode - 1].alpha_bits > color_modes[pf2.accum_mode - 1].alpha_bits) |
| return kCFCompareGreaterThan; |
| } |
| else |
| return kCFCompareGreaterThan; |
| } |
| else if (pf2.accum_mode) |
| return kCFCompareLessThan; |
| |
| /* Fewer auxiliary buffers before more auxiliary buffers. (This is a guess.) */ |
| if (pf1.aux_buffers < pf2.aux_buffers) |
| return kCFCompareLessThan; |
| if (pf1.aux_buffers > pf2.aux_buffers) |
| return kCFCompareGreaterThan; |
| |
| /* If we get here, arbitrarily sort based on code. */ |
| if (code1 < code2) |
| return kCFCompareLessThan; |
| if (code1 > code2) |
| return kCFCompareGreaterThan; |
| return kCFCompareEqualTo; |
| } |
| |
| |
| static BOOL init_pixel_formats(void) |
| { |
| BOOL ret = FALSE; |
| CGLRendererInfoObj renderer_info; |
| GLint rendererCount; |
| CGLError err; |
| CFMutableSetRef pixel_format_set; |
| CFMutableArrayRef pixel_format_array; |
| int i; |
| CFRange range; |
| |
| TRACE("()\n"); |
| |
| err = CGLQueryRendererInfo(CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()), &renderer_info, &rendererCount); |
| if (err) |
| { |
| WARN("CGLQueryRendererInfo failed (%d) %s\n", err, CGLErrorString(err)); |
| return FALSE; |
| } |
| |
| pixel_format_set = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); |
| if (!pixel_format_set) |
| { |
| WARN("CFSetCreateMutable failed\n"); |
| CGLDestroyRendererInfo(renderer_info); |
| return FALSE; |
| } |
| |
| pixel_format_array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); |
| if (!pixel_format_array) |
| { |
| WARN("CFArrayCreateMutable failed\n"); |
| CFRelease(pixel_format_set); |
| CGLDestroyRendererInfo(renderer_info); |
| return FALSE; |
| } |
| |
| for (i = 0; i < rendererCount; i++) |
| { |
| renderer_properties renderer; |
| |
| get_renderer_properties(renderer_info, i, &renderer); |
| if (TRACE_ON(wgl)) |
| { |
| TRACE("renderer_properties %d:\n", i); |
| dump_renderer(&renderer); |
| } |
| |
| enum_renderer_pixel_formats(renderer, pixel_format_array, pixel_format_set); |
| } |
| |
| CFRelease(pixel_format_set); |
| CGLDestroyRendererInfo(renderer_info); |
| |
| range = CFRangeMake(0, CFArrayGetCount(pixel_format_array)); |
| if (range.length) |
| { |
| pixel_formats = HeapAlloc(GetProcessHeap(), 0, range.length * sizeof(*pixel_formats)); |
| if (pixel_formats) |
| { |
| CFArraySortValues(pixel_format_array, range, pixel_format_comparator, NULL); |
| for (i = 0; i < range.length; i++) |
| { |
| CFNumberRef number = CFArrayGetValueAtIndex(pixel_format_array, i); |
| UInt64 code; |
| |
| CFNumberGetValue(number, kCFNumberLongLongType, &code); |
| pixel_formats[i] = pixel_format_for_code(code); |
| if (pixel_formats[i].window) |
| nb_displayable_formats++; |
| } |
| |
| nb_formats = range.length; |
| TRACE("Total number of unique pixel formats: %d\n", nb_formats); |
| ret = TRUE; |
| } |
| else |
| WARN("failed to allocate pixel format list\n"); |
| } |
| else |
| WARN("got no pixel formats\n"); |
| |
| CFRelease(pixel_format_array); |
| return ret; |
| } |
| |
| |
| static inline BOOL is_valid_pixel_format(int format) |
| { |
| return format > 0 && format <= nb_formats; |
| } |
| |
| |
| static inline BOOL is_displayable_pixel_format(int format) |
| { |
| return format > 0 && format <= nb_displayable_formats; |
| } |
| |
| |
| static const pixel_format *get_pixel_format(int format, BOOL allow_nondisplayable) |
| { |
| /* Check if the pixel format is valid. Note that it is legal to pass an invalid |
| * format in case of probing the number of pixel formats. |
| */ |
| if (is_valid_pixel_format(format) && (is_displayable_pixel_format(format) || allow_nondisplayable)) |
| { |
| TRACE("Returning format %d\n", format); |
| return &pixel_formats[format - 1]; |
| } |
| return NULL; |
| } |
| |
| |
| static BOOL init_gl_info(void) |
| { |
| static const char legacy_extensions[] = " WGL_EXT_extensions_string"; |
| static const char legacy_ext_swap_control[] = " WGL_EXT_swap_control"; |
| |
| CGDirectDisplayID display = CGMainDisplayID(); |
| CGOpenGLDisplayMask displayMask = CGDisplayIDToOpenGLDisplayMask(display); |
| CGLPixelFormatAttribute attribs[] = { |
| kCGLPFADisplayMask, displayMask, |
| 0 |
| }; |
| #if defined(MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 |
| CGLPixelFormatAttribute core_attribs[] = |
| { |
| kCGLPFADisplayMask, displayMask, |
| kCGLPFAAccelerated, |
| kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)kCGLOGLPVersion_3_2_Core, |
| 0 |
| }; |
| #endif |
| CGLPixelFormatObj pix; |
| GLint virtualScreens; |
| CGLError err; |
| CGLContextObj context; |
| CGLContextObj old_context = CGLGetCurrentContext(); |
| const char *str; |
| size_t length; |
| |
| err = CGLChoosePixelFormat(attribs, &pix, &virtualScreens); |
| if (err != kCGLNoError || !pix) |
| { |
| WARN("CGLChoosePixelFormat() failed with error %d %s\n", err, CGLErrorString(err)); |
| return FALSE; |
| } |
| |
| err = CGLCreateContext(pix, NULL, &context); |
| CGLReleasePixelFormat(pix); |
| if (err != kCGLNoError || !context) |
| { |
| WARN("CGLCreateContext() failed with error %d %s\n", err, CGLErrorString(err)); |
| return FALSE; |
| } |
| |
| err = CGLSetCurrentContext(context); |
| if (err != kCGLNoError) |
| { |
| WARN("CGLSetCurrentContext() failed with error %d %s\n", err, CGLErrorString(err)); |
| CGLReleaseContext(context); |
| return FALSE; |
| } |
| |
| str = (const char*)opengl_funcs.gl.p_glGetString(GL_EXTENSIONS); |
| length = strlen(str) + sizeof(legacy_extensions); |
| if (allow_vsync) |
| length += strlen(legacy_ext_swap_control); |
| gl_info.glExtensions = HeapAlloc(GetProcessHeap(), 0, length); |
| strcpy(gl_info.glExtensions, str); |
| strcat(gl_info.glExtensions, legacy_extensions); |
| if (allow_vsync) |
| strcat(gl_info.glExtensions, legacy_ext_swap_control); |
| |
| opengl_funcs.gl.p_glGetIntegerv(GL_MAX_VIEWPORT_DIMS, gl_info.max_viewport_dims); |
| |
| str = (const char*)opengl_funcs.gl.p_glGetString(GL_VERSION); |
| sscanf(str, "%u.%u", &gl_info.max_major, &gl_info.max_minor); |
| TRACE("GL version : %s\n", str); |
| TRACE("GL renderer : %s\n", opengl_funcs.gl.p_glGetString(GL_RENDERER)); |
| |
| CGLSetCurrentContext(old_context); |
| CGLReleaseContext(context); |
| |
| #if defined(MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 |
| err = CGLChoosePixelFormat(core_attribs, &pix, &virtualScreens); |
| if (err != kCGLNoError || !pix) |
| { |
| WARN("CGLChoosePixelFormat() for a core context failed with error %d %s\n", |
| err, CGLErrorString(err)); |
| return TRUE; |
| } |
| |
| err = CGLCreateContext(pix, NULL, &context); |
| CGLReleasePixelFormat(pix); |
| if (err != kCGLNoError || !context) |
| { |
| WARN("CGLCreateContext() for a core context failed with error %d %s\n", |
| err, CGLErrorString(err)); |
| return TRUE; |
| } |
| |
| err = CGLSetCurrentContext(context); |
| if (err != kCGLNoError) |
| { |
| WARN("CGLSetCurrentContext() for a core context failed with error %d %s\n", |
| err, CGLErrorString(err)); |
| CGLReleaseContext(context); |
| return TRUE; |
| } |
| |
| str = (const char*)opengl_funcs.gl.p_glGetString(GL_VERSION); |
| TRACE("Core context GL version: %s\n", str); |
| sscanf(str, "%u.%u", &gl_info.max_major, &gl_info.max_minor); |
| CGLSetCurrentContext(old_context); |
| CGLReleaseContext(context); |
| #endif |
| |
| return TRUE; |
| } |
| |
| |
| static int get_dc_pixel_format(HDC hdc) |
| { |
| int format; |
| HWND hwnd; |
| |
| if ((hwnd = WindowFromDC(hdc))) |
| { |
| struct macdrv_win_data *data; |
| |
| if (!(data = get_win_data(hwnd))) |
| { |
| FIXME("DC for window %p of other process: not implemented\n", hwnd); |
| return 0; |
| } |
| |
| format = data->pixel_format; |
| release_win_data(data); |
| } |
| else |
| { |
| struct wgl_pbuffer *pbuffer; |
| |
| EnterCriticalSection(&dc_pbuffers_section); |
| pbuffer = (struct wgl_pbuffer*)CFDictionaryGetValue(dc_pbuffers, hdc); |
| if (pbuffer) |
| format = pbuffer->format; |
| else |
| { |
| WARN("no window or pbuffer for DC %p\n", hdc); |
| format = 0; |
| } |
| LeaveCriticalSection(&dc_pbuffers_section); |
| } |
| |
| return format; |
| } |
| |
| |
| /********************************************************************** |
| * create_context |
| */ |
| static BOOL create_context(struct wgl_context *context, CGLContextObj share, unsigned int major) |
| { |
| const pixel_format *pf; |
| CGLPixelFormatAttribute attribs[64]; |
| int n = 0; |
| CGLPixelFormatObj pix; |
| GLint virtualScreens; |
| CGLError err; |
| BOOL core = major >= 3; |
| |
| #if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 |
| if (core) |
| { |
| WARN("OS X version >= 10.7 is required to be able to create core contexts\n"); |
| return FALSE; |
| } |
| #endif |
| |
| pf = get_pixel_format(context->format, TRUE /* non-displayable */); |
| if (!pf) |
| { |
| ERR("Invalid pixel format %d, expect problems!\n", context->format); |
| SetLastError(ERROR_INVALID_PIXEL_FORMAT); |
| return FALSE; |
| } |
| |
| attribs[n++] = kCGLPFAMinimumPolicy; |
| attribs[n++] = kCGLPFAClosestPolicy; |
| |
| if (pf->accelerated) |
| { |
| attribs[n++] = kCGLPFAAccelerated; |
| attribs[n++] = kCGLPFANoRecovery; |
| } |
| else |
| { |
| attribs[n++] = kCGLPFARendererID; |
| attribs[n++] = kCGLRendererGenericFloatID; |
| } |
| |
| if (pf->double_buffer) |
| attribs[n++] = kCGLPFADoubleBuffer; |
| |
| if (!core) |
| { |
| attribs[n++] = kCGLPFAAuxBuffers; |
| attribs[n++] = pf->aux_buffers; |
| } |
| |
| attribs[n++] = kCGLPFAColorSize; |
| attribs[n++] = color_modes[pf->color_mode].color_bits; |
| attribs[n++] = kCGLPFAAlphaSize; |
| attribs[n++] = color_modes[pf->color_mode].alpha_bits; |
| if (color_modes[pf->color_mode].is_float) |
| attribs[n++] = kCGLPFAColorFloat; |
| |
| attribs[n++] = kCGLPFADepthSize; |
| attribs[n++] = pf->depth_bits; |
| |
| attribs[n++] = kCGLPFAStencilSize; |
| attribs[n++] = pf->stencil_bits; |
| |
| if (pf->stereo) |
| attribs[n++] = kCGLPFAStereo; |
| |
| if (pf->accum_mode && !core) |
| { |
| attribs[n++] = kCGLPFAAccumSize; |
| attribs[n++] = color_modes[pf->accum_mode - 1].color_bits; |
| } |
| |
| if (pf->pbuffer && !core) |
| attribs[n++] = kCGLPFAPBuffer; |
| |
| if (pf->sample_buffers && pf->samples) |
| { |
| attribs[n++] = kCGLPFASampleBuffers; |
| attribs[n++] = pf->sample_buffers; |
| attribs[n++] = kCGLPFASamples; |
| attribs[n++] = pf->samples; |
| } |
| |
| if (pf->backing_store) |
| attribs[n++] = kCGLPFABackingStore; |
| |
| #if defined(MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 |
| if (core) |
| { |
| attribs[n++] = kCGLPFAOpenGLProfile; |
| #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 |
| if (major == 3) |
| attribs[n++] = (int)kCGLOGLPVersion_GL3_Core; |
| else |
| attribs[n++] = (int)kCGLOGLPVersion_GL4_Core; |
| #else |
| attribs[n++] = (int)kCGLOGLPVersion_3_2_Core; |
| #endif |
| } |
| #endif |
| |
| attribs[n] = 0; |
| |
| err = CGLChoosePixelFormat(attribs, &pix, &virtualScreens); |
| if (err != kCGLNoError || !pix) |
| { |
| WARN("CGLChoosePixelFormat() failed with error %d %s\n", err, CGLErrorString(err)); |
| SetLastError(ERROR_INVALID_OPERATION); |
| return FALSE; |
| } |
| |
| err = CGLCreateContext(pix, share, &context->cglcontext); |
| CGLReleasePixelFormat(pix); |
| if (err != kCGLNoError || !context->cglcontext) |
| { |
| context->cglcontext = NULL; |
| WARN("CGLCreateContext() failed with error %d %s\n", err, CGLErrorString(err)); |
| SetLastError(ERROR_INVALID_OPERATION); |
| return FALSE; |
| } |
| |
| if (gl_surface_mode == GL_SURFACE_IN_FRONT_TRANSPARENT) |
| { |
| GLint opacity = 0; |
| err = CGLSetParameter(context->cglcontext, kCGLCPSurfaceOpacity, &opacity); |
| if (err != kCGLNoError) |
| WARN("CGLSetParameter(kCGLCPSurfaceOpacity) failed with error %d %s; leaving opaque\n", err, CGLErrorString(err)); |
| } |
| else if (gl_surface_mode == GL_SURFACE_BEHIND) |
| { |
| GLint order = -1; |
| err = CGLSetParameter(context->cglcontext, kCGLCPSurfaceOrder, &order); |
| if (err != kCGLNoError) |
| WARN("CGLSetParameter(kCGLCPSurfaceOrder) failed with error %d %s; leaving in front\n", err, CGLErrorString(err)); |
| } |
| |
| context->context = macdrv_create_opengl_context(context->cglcontext); |
| CGLReleaseContext(context->cglcontext); |
| if (!context->context) |
| { |
| WARN("macdrv_create_opengl_context() failed\n"); |
| SetLastError(ERROR_INVALID_OPERATION); |
| return FALSE; |
| } |
| context->major = major; |
| |
| if (allow_vsync) |
| InterlockedExchange(&context->update_swap_interval, TRUE); |
| |
| TRACE("created context %p/%p/%p\n", context, context->context, context->cglcontext); |
| |
| return TRUE; |
| } |
| |
| |
| static BOOL get_gl_view_window_rect(struct macdrv_win_data *data, macdrv_window *window, RECT *rect) |
| { |
| BOOL ret = TRUE; |
| *rect = data->client_rect; |
| |
| if (data->cocoa_window) |
| { |
| if (window) |
| *window = data->cocoa_window; |
| OffsetRect(rect, -data->whole_rect.left, -data->whole_rect.top); |
| } |
| else |
| { |
| HWND top = GetAncestor(data->hwnd, GA_ROOT); |
| HWND parent = GetAncestor(data->hwnd, GA_PARENT); |
| struct macdrv_win_data *top_data = get_win_data(top); |
| |
| if (top_data && top_data->cocoa_window) |
| { |
| if (window) |
| *window = top_data->cocoa_window; |
| MapWindowPoints(parent, 0, (POINT*)rect, 2); |
| OffsetRect(rect, -top_data->whole_rect.left, -top_data->whole_rect.top); |
| } |
| else |
| ret = FALSE; |
| |
| release_win_data(top_data); |
| } |
| |
| return ret; |
| } |
| |
| |
| /*********************************************************************** |
| * set_win_format |
| */ |
| static BOOL set_win_format(struct macdrv_win_data *data, int format) |
| { |
| TRACE("hwnd %p format %d\n", data->hwnd, format); |
| |
| if (!data->gl_view) |
| { |
| macdrv_window cocoa_window; |
| |
| if (!get_gl_view_window_rect(data, &cocoa_window, &data->gl_rect)) |
| { |
| ERR("no top-level parent with Cocoa window in this process\n"); |
| return FALSE; |
| } |
| |
| data->gl_view = macdrv_create_view(cocoa_window, cgrect_from_rect(data->gl_rect)); |
| if (!data->gl_view) |
| { |
| WARN("failed to create GL view for window %p rect %s\n", cocoa_window, wine_dbgstr_rect(&data->gl_rect)); |
| return FALSE; |
| } |
| |
| TRACE("created GL view %p in window %p at %s\n", data->gl_view, cocoa_window, |
| wine_dbgstr_rect(&data->gl_rect)); |
| } |
| |
| data->pixel_format = format; |
| |
| return TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * set_pixel_format |
| * |
| * Implementation of wglSetPixelFormat and wglSetPixelFormatWINE. |
| */ |
| static BOOL set_pixel_format(HDC hdc, int fmt, BOOL allow_reset) |
| { |
| struct macdrv_win_data *data; |
| const pixel_format *pf; |
| HWND hwnd = WindowFromDC(hdc); |
| BOOL ret = FALSE; |
| |
| TRACE("hdc %p format %d\n", hdc, fmt); |
| |
| if (!hwnd || hwnd == GetDesktopWindow()) |
| { |
| WARN("not a proper window DC %p/%p\n", hdc, hwnd); |
| return FALSE; |
| } |
| |
| if (!(data = get_win_data(hwnd))) |
| { |
| FIXME("DC for window %p of other process: not implemented\n", hwnd); |
| return FALSE; |
| } |
| |
| if (!allow_reset && data->pixel_format) /* cannot change it if already set */ |
| { |
| ret = (data->pixel_format == fmt); |
| goto done; |
| } |
| |
| /* Check if fmt is in our list of supported formats to see if it is supported. */ |
| pf = get_pixel_format(fmt, FALSE /* non-displayable */); |
| if (!pf) |
| { |
| ERR("Invalid pixel format: %d\n", fmt); |
| goto done; |
| } |
| |
| if (!pf->window) |
| { |
| WARN("Pixel format %d is not compatible for window rendering\n", fmt); |
| goto done; |
| } |
| |
| if (!set_win_format(data, fmt)) |
| { |
| WARN("Couldn't set format of the window, returning failure\n"); |
| goto done; |
| } |
| |
| TRACE("pixel format:\n"); |
| TRACE(" window: %u\n", (unsigned int)pf->window); |
| TRACE(" pBuffer: %u\n", (unsigned int)pf->pbuffer); |
| TRACE(" accelerated: %u\n", (unsigned int)pf->accelerated); |
| TRACE(" color bits: %u%s\n", (unsigned int)color_modes[pf->color_mode].color_bits, (color_modes[pf->color_mode].is_float ? " float" : "")); |
| TRACE(" alpha bits: %u\n", (unsigned int)color_modes[pf->color_mode].alpha_bits); |
| TRACE(" aux buffers: %u\n", (unsigned int)pf->aux_buffers); |
| TRACE(" depth bits: %u\n", (unsigned int)pf->depth_bits); |
| TRACE(" stencil bits: %u\n", (unsigned int)pf->stencil_bits); |
| TRACE(" accum bits: %u\n", (unsigned int)pf->accum_mode ? color_modes[pf->accum_mode - 1].color_bits : 0); |
| TRACE(" double_buffer: %u\n", (unsigned int)pf->double_buffer); |
| TRACE(" stereo: %u\n", (unsigned int)pf->stereo); |
| TRACE(" sample_buffers: %u\n", (unsigned int)pf->sample_buffers); |
| TRACE(" samples: %u\n", (unsigned int)pf->samples); |
| TRACE(" backing_store: %u\n", (unsigned int)pf->backing_store); |
| ret = TRUE; |
| |
| done: |
| release_win_data(data); |
| if (ret && gl_surface_mode == GL_SURFACE_BEHIND) __wine_set_pixel_format(hwnd, fmt); |
| return ret; |
| } |
| |
| |
| /********************************************************************** |
| * set_gl_view_parent |
| */ |
| void set_gl_view_parent(HWND hwnd, HWND parent) |
| { |
| struct macdrv_win_data *data; |
| |
| if (!(data = get_win_data(hwnd))) return; |
| |
| if (data->gl_view) |
| { |
| macdrv_window cocoa_window; |
| |
| TRACE("moving GL view %p to parent %p\n", data->gl_view, parent); |
| |
| if (!get_gl_view_window_rect(data, &cocoa_window, &data->gl_rect)) |
| { |
| ERR("no top-level parent with Cocoa window in this process\n"); |
| macdrv_dispose_view(data->gl_view); |
| data->gl_view = NULL; |
| release_win_data(data); |
| __wine_set_pixel_format( hwnd, 0 ); |
| return; |
| } |
| |
| macdrv_set_view_window_and_frame(data->gl_view, cocoa_window, cgrect_from_rect(data->gl_rect)); |
| } |
| |
| release_win_data(data); |
| } |
| |
| |
| /********************************************************************** |
| * make_context_current |
| */ |
| static void make_context_current(struct wgl_context *context, BOOL read) |
| { |
| macdrv_view view; |
| struct wgl_pbuffer *pbuffer; |
| |
| if (read) |
| { |
| view = context->read_view; |
| pbuffer = context->read_pbuffer; |
| } |
| else |
| { |
| view = context->draw_view; |
| pbuffer = context->draw_pbuffer; |
| } |
| |
| if (view || !pbuffer) |
| macdrv_make_context_current(context->context, view); |
| else |
| { |
| CGLSetPBuffer(context->cglcontext, pbuffer->pbuffer, pbuffer->face, |
| pbuffer->level, 0); |
| CGLSetCurrentContext(context->cglcontext); |
| } |
| } |
| |
| |
| /********************************************************************** |
| * set_swap_interval |
| */ |
| static BOOL set_swap_interval(struct wgl_context *context, long interval) |
| { |
| CGLError err; |
| |
| /* In theory, for single-buffered contexts, there's no such thing as a swap |
| so the swap interval shouldn't matter. But OS X will synchronize flushes |
| of single-buffered contexts if the interval is set to non-zero. */ |
| if (interval && !pixel_formats[context->format - 1].double_buffer) |
| interval = 0; |
| |
| err = CGLSetParameter(context->cglcontext, kCGLCPSwapInterval, (GLint*)&interval); |
| if (err != kCGLNoError) |
| WARN("CGLSetParameter(kCGLCPSwapInterval) failed; error %d %s\n", err, CGLErrorString(err)); |
| |
| return err == kCGLNoError; |
| } |
| |
| |
| /********************************************************************** |
| * sync_swap_interval |
| */ |
| static void sync_swap_interval(struct wgl_context *context) |
| { |
| if (InterlockedCompareExchange(&context->update_swap_interval, FALSE, TRUE)) |
| { |
| int interval; |
| |
| if (context->draw_hwnd) |
| { |
| struct macdrv_win_data *data = get_win_data(context->draw_hwnd); |
| if (data) |
| { |
| interval = data->swap_interval; |
| release_win_data(data); |
| } |
| else /* window was destroyed? */ |
| interval = 1; |
| } |
| else /* pbuffer */ |
| interval = 0; |
| |
| set_swap_interval(context, interval); |
| } |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_glCopyColorTable |
| * |
| * Hook into glCopyColorTable as part of the implementation of |
| * wglMakeContextCurrentARB. If the context has a separate readable, |
| * temporarily make that current, do glCopyColorTable, and then set it |
| * back to the drawable. This is modeled after what Mesa GLX's Apple |
| * implementation does. |
| */ |
| static void macdrv_glCopyColorTable(GLenum target, GLenum internalformat, GLint x, GLint y, |
| GLsizei width) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| |
| if (context->read_view || context->read_pbuffer) |
| make_context_current(context, TRUE); |
| |
| pglCopyColorTable(target, internalformat, x, y, width); |
| |
| if (context->read_view || context->read_pbuffer) |
| make_context_current(context, FALSE); |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_glCopyPixels |
| * |
| * Hook into glCopyPixels as part of the implementation of |
| * wglMakeContextCurrentARB. If the context has a separate readable, |
| * temporarily make that current, do glCopyPixels, and then set it back |
| * to the drawable. This is modeled after what Mesa GLX's Apple |
| * implementation does. |
| */ |
| static void macdrv_glCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| |
| if (context->read_view || context->read_pbuffer) |
| make_context_current(context, TRUE); |
| |
| pglCopyPixels(x, y, width, height, type); |
| |
| if (context->read_view || context->read_pbuffer) |
| make_context_current(context, FALSE); |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_glFinish |
| */ |
| static void macdrv_glFinish(void) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| |
| sync_swap_interval(context); |
| pglFinish(); |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_glFlush |
| */ |
| static void macdrv_glFlush(void) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| |
| sync_swap_interval(context); |
| |
| if (skip_single_buffer_flushes) |
| { |
| const pixel_format *pf = &pixel_formats[context->format - 1]; |
| DWORD now = GetTickCount(); |
| |
| TRACE("double buffer %d last flush time %d now %d\n", (int)pf->double_buffer, |
| context->last_flush_time, now); |
| if (pglFlushRenderAPPLE && !pf->double_buffer && (now - context->last_flush_time) < 17) |
| { |
| TRACE("calling glFlushRenderAPPLE()\n"); |
| pglFlushRenderAPPLE(); |
| return; |
| } |
| else |
| { |
| TRACE("calling glFlush()\n"); |
| context->last_flush_time = now; |
| } |
| } |
| |
| pglFlush(); |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_glGetString |
| * |
| * Hook into glGetString in order to return some legacy WGL extensions |
| * that couldn't be advertised via the standard |
| * WGL_ARB_extensions_string mechanism. Some programs, especially |
| * older ones, expect to find certain older extensions, such as |
| * WGL_EXT_extensions_string itself, in the standard GL extensions |
| * string, and won't query any other WGL extensions unless they find |
| * that particular extension there. |
| */ |
| static const GLubyte *macdrv_glGetString(GLenum name) |
| { |
| if (name == GL_EXTENSIONS && gl_info.glExtensions) |
| return (const GLubyte *)gl_info.glExtensions; |
| else |
| return pglGetString(name); |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_glReadPixels |
| * |
| * Hook into glReadPixels as part of the implementation of |
| * wglMakeContextCurrentARB. If the context has a separate readable, |
| * temporarily make that current, do glReadPixels, and then set it back |
| * to the drawable. This is modeled after what Mesa GLX's Apple |
| * implementation does. |
| */ |
| static void macdrv_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, |
| GLenum format, GLenum type, void *pixels) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| |
| if (context->read_view || context->read_pbuffer) |
| make_context_current(context, TRUE); |
| |
| pglReadPixels(x, y, width, height, format, type, pixels); |
| |
| if (context->read_view || context->read_pbuffer) |
| make_context_current(context, FALSE); |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_glViewport |
| * |
| * Hook into glViewport as an opportunity to update the OpenGL context |
| * if necessary. This is modeled after what Mesa GLX's Apple |
| * implementation does. |
| */ |
| static void macdrv_glViewport(GLint x, GLint y, GLsizei width, GLsizei height) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| |
| macdrv_update_opengl_context(context->context); |
| pglViewport(x, y, width, height); |
| } |
| |
| |
| /*********************************************************************** |
| * macdrv_wglBindTexImageARB |
| * |
| * WGL_ARB_render_texture: wglBindTexImageARB |
| */ |
| static BOOL macdrv_wglBindTexImageARB(struct wgl_pbuffer *pbuffer, int iBuffer) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| GLenum source; |
| CGLError err; |
| |
| TRACE("pbuffer %p iBuffer 0x%x\n", pbuffer, iBuffer); |
| |
| if (pbuffer->no_texture) |
| { |
| SetLastError(ERROR_INVALID_OPERATION); |
| return GL_FALSE; |
| } |
| |
| if (!context->draw_view && context->draw_pbuffer == pbuffer) |
| opengl_funcs.gl.p_glFlush(); |
| |
| switch (iBuffer) |
| { |
| case WGL_FRONT_LEFT_ARB: |
| if (pixel_formats[pbuffer->format - 1].stereo) |
| source = GL_FRONT_LEFT; |
| else |
| source = GL_FRONT; |
| break; |
| case WGL_FRONT_RIGHT_ARB: |
| source = GL_FRONT_RIGHT; |
| break; |
| case WGL_BACK_LEFT_ARB: |
| if (pixel_formats[pbuffer->format - 1].stereo) |
| source = GL_BACK_LEFT; |
| else |
| source = GL_BACK; |
| break; |
| case WGL_BACK_RIGHT_ARB: |
| source = GL_BACK_RIGHT; |
| break; |
| case WGL_AUX0_ARB: source = GL_AUX0; break; |
| case WGL_AUX1_ARB: source = GL_AUX1; break; |
| case WGL_AUX2_ARB: source = GL_AUX2; break; |
| case WGL_AUX3_ARB: source = GL_AUX3; break; |
| |
| case WGL_AUX4_ARB: |
| case WGL_AUX5_ARB: |
| case WGL_AUX6_ARB: |
| case WGL_AUX7_ARB: |
| case WGL_AUX8_ARB: |
| case WGL_AUX9_ARB: |
| FIXME("unsupported source buffer 0x%x\n", iBuffer); |
| SetLastError(ERROR_INVALID_DATA); |
| return GL_FALSE; |
| |
| default: |
| WARN("unknown source buffer 0x%x\n", iBuffer); |
| SetLastError(ERROR_INVALID_DATA); |
| return GL_FALSE; |
| } |
| |
| err = CGLTexImagePBuffer(context->cglcontext, pbuffer->pbuffer, source); |
| if (err != kCGLNoError) |
| { |
| WARN("CGLTexImagePBuffer failed with err %d %s\n", err, CGLErrorString(err)); |
| SetLastError(ERROR_INVALID_OPERATION); |
| return GL_FALSE; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| |
| /*********************************************************************** |
| * macdrv_wglChoosePixelFormatARB |
| * |
| * WGL_ARB_pixel_format: wglChoosePixelFormatARB |
| */ |
| static BOOL macdrv_wglChoosePixelFormatARB(HDC hdc, const int *piAttribIList, |
| const FLOAT *pfAttribFList, UINT nMaxFormats, |
| int *piFormats, UINT *nNumFormats) |
| { |
| pixel_format pf, valid; |
| const int *iptr; |
| int color_bits, red_bits, green_bits, blue_bits, alpha_bits; |
| int accum_bits, accum_red_bits, accum_green_bits, accum_blue_bits, accum_alpha_bits; |
| int float_color; |
| BOOL srgb; |
| int i, found = 0; |
| |
| TRACE("hdc %p piAttribIList %p pfAttribFList %p nMaxFormats %u piFormats %p nNumFormats %p\n", |
| hdc, piAttribIList, pfAttribFList, nMaxFormats, piFormats, nNumFormats); |
| if (pfAttribFList) |
| FIXME("unused pfAttribFList\n"); |
| |
| memset(&pf, 0, sizeof(pf)); |
| memset(&valid, 0, sizeof(valid)); |
| color_bits = red_bits = green_bits = blue_bits = alpha_bits = 0; |
| accum_bits = accum_red_bits = accum_green_bits = accum_blue_bits = accum_alpha_bits = 0; |
| float_color = -1; |
| srgb = FALSE; |
| |
| for (iptr = piAttribIList; iptr && *iptr; iptr += 2) |
| { |
| int attr = iptr[0]; |
| int value = iptr[1]; |
| |
| TRACE("%s\n", debugstr_attrib(attr, value)); |
| |
| switch (attr) |
| { |
| case WGL_DRAW_TO_WINDOW_ARB: |
| if (valid.window && (!pf.window != !value)) goto cant_match; |
| pf.window = (value != 0); |
| valid.window = 1; |
| break; |
| |
| case WGL_DRAW_TO_BITMAP_ARB: |
| goto cant_match; |
| |
| case WGL_ACCELERATION_ARB: |
| if (value == WGL_FULL_ACCELERATION_ARB) |
| value = 1; |
| else if (value == WGL_NO_ACCELERATION_ARB) |
| value = 0; |
| else |
| goto cant_match; |
| if (valid.accelerated && pf.accelerated != value) goto cant_match; |
| pf.accelerated = value; |
| valid.accelerated = 1; |
| break; |
| |
| case WGL_NEED_PALETTE_ARB: |
| case WGL_NEED_SYSTEM_PALETTE_ARB: |
| case WGL_SWAP_LAYER_BUFFERS_ARB: |
| if (value) goto cant_match; |
| break; |
| |
| case WGL_SWAP_METHOD_ARB: |
| if (value == WGL_SWAP_COPY_ARB) |
| value = 1; |
| else if (value == WGL_SWAP_UNDEFINED_ARB) |
| value = 0; |
| else |
| goto cant_match; |
| if (valid.backing_store && pf.backing_store != value) goto cant_match; |
| if (valid.double_buffer && !pf.double_buffer && value) goto cant_match; |
| pf.backing_store = value; |
| valid.backing_store = 1; |
| break; |
| |
| case WGL_NUMBER_OVERLAYS_ARB: |
| case WGL_NUMBER_UNDERLAYS_ARB: |
| if (value) goto cant_match; |
| break; |
| |
| case WGL_SHARE_DEPTH_ARB: |
| case WGL_SHARE_STENCIL_ARB: |
| case WGL_SHARE_ACCUM_ARB: |
| /* no effect */ |
| break; |
| |
| case WGL_SUPPORT_GDI_ARB: |
| if (value) goto cant_match; |
| break; |
| |
| case WGL_SUPPORT_OPENGL_ARB: |
| if (!value) goto cant_match; |
| break; |
| |
| case WGL_DOUBLE_BUFFER_ARB: |
| if (valid.double_buffer && (!pf.double_buffer != !value)) goto cant_match; |
| pf.double_buffer = (value != 0); |
| valid.double_buffer = 1; |
| if (valid.backing_store && pf.backing_store && !pf.double_buffer) goto cant_match; |
| break; |
| |
| case WGL_STEREO_ARB: |
| if (valid.stereo && (!pf.stereo != !value)) goto cant_match; |
| pf.stereo = (value != 0); |
| valid.stereo = 1; |
| break; |
| |
| case WGL_PIXEL_TYPE_ARB: |
| if (value == WGL_TYPE_RGBA_FLOAT_ARB) |
| value = 1; |
| else if (value == WGL_TYPE_RGBA_ARB) |
| value = 0; |
| else |
| { |
| /* Mac contexts don't support rendering to unsigned floating |
| point formats, even if GL_EXT_packed_float is supported. |
| So, WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT is not supported. */ |
| goto cant_match; |
| } |
| if (float_color != -1 && float_color != value) goto cant_match; |
| if (srgb && value) goto cant_match; |
| float_color = value; |
| break; |
| |
| case WGL_COLOR_BITS_ARB: |
| if (color_bits < value) color_bits = value; |
| break; |
| |
| case WGL_RED_BITS_ARB: |
| if (srgb && value > 8) goto cant_match; |
| if (red_bits < value) red_bits = value; |
| break; |
| |
| case WGL_GREEN_BITS_ARB: |
| if (srgb && value > 8) goto cant_match; |
| if (green_bits < value) green_bits = value; |
| break; |
| |
| case WGL_BLUE_BITS_ARB: |
| if (srgb && value > 8) goto cant_match; |
| if (blue_bits < value) blue_bits = value; |
| break; |
| |
| case WGL_ALPHA_BITS_ARB: |
| if (alpha_bits < value) alpha_bits = value; |
| break; |
| |
| case WGL_ACCUM_BITS_ARB: |
| if (accum_bits < value) accum_bits = value; |
| break; |
| |
| case WGL_ACCUM_RED_BITS_ARB: |
| if (accum_red_bits < value) accum_red_bits = value; |
| break; |
| |
| case WGL_ACCUM_GREEN_BITS_ARB: |
| if (accum_green_bits < value) accum_green_bits = value; |
| break; |
| |
| case WGL_ACCUM_BLUE_BITS_ARB: |
| if (accum_blue_bits < value) accum_blue_bits = value; |
| break; |
| |
| case WGL_ACCUM_ALPHA_BITS_ARB: |
| if (accum_alpha_bits < value) accum_alpha_bits = value; |
| break; |
| |
| case WGL_DEPTH_BITS_ARB: |
| if (value > 255) goto cant_match; |
| if (pf.depth_bits < value) pf.depth_bits = value; |
| break; |
| |
| case WGL_STENCIL_BITS_ARB: |
| if (value > 255) goto cant_match; |
| if (pf.stencil_bits < value) pf.stencil_bits = value; |
| break; |
| |
| case WGL_AUX_BUFFERS_ARB: |
| if (value > 7) goto cant_match; |
| if (pf.aux_buffers < value) pf.aux_buffers = value; |
| break; |
| |
| case WGL_SAMPLE_BUFFERS_ARB: |
| if (value > 1) goto cant_match; |
| if (pf.sample_buffers < value) pf.sample_buffers = value; |
| break; |
| |
| case WGL_SAMPLES_ARB: |
| if (value > 31) goto cant_match; |
| if (pf.samples < value) pf.samples = value; |
| break; |
| |
| case WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB: /* a.k.a. WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT */ |
| /* sRGB is only supported for 8-bit integer color components */ |
| if (float_color >= 1 || red_bits > 8 || green_bits > 8 || blue_bits > 8) |
| goto cant_match; |
| srgb = TRUE; |
| break; |
| |
| case WGL_NUMBER_PIXEL_FORMATS_ARB: |
| case WGL_RED_SHIFT_ARB: |
| case WGL_GREEN_SHIFT_ARB: |
| case WGL_BLUE_SHIFT_ARB: |
| case WGL_ALPHA_SHIFT_ARB: |
| case WGL_TRANSPARENT_ARB: |
| case WGL_TRANSPARENT_RED_VALUE_ARB: |
| case WGL_TRANSPARENT_GREEN_VALUE_ARB: |
| case WGL_TRANSPARENT_BLUE_VALUE_ARB: |
| case WGL_TRANSPARENT_ALPHA_VALUE_ARB: |
| case WGL_TRANSPARENT_INDEX_VALUE_ARB: |
| /* ignored */ |
| break; |
| |
| case WGL_DRAW_TO_PBUFFER_ARB: |
| case WGL_BIND_TO_TEXTURE_RGB_ARB: |
| case WGL_BIND_TO_TEXTURE_RGBA_ARB: |
| case WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV: |
| case WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV: |
| if (valid.pbuffer && (!pf.pbuffer != !value)) goto cant_match; |
| pf.pbuffer = (value != 0); |
| valid.pbuffer = 1; |
| if ((attr == WGL_BIND_TO_TEXTURE_RGBA_ARB || attr == WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV) && |
| !alpha_bits) |
| alpha_bits = 1; |
| break; |
| |
| default: |
| WARN("invalid attribute %s\n", debugstr_attrib(attr, value)); |
| return GL_FALSE; |
| } |
| } |
| |
| TRACE("required: w/p/a %s/%s/%s col/r/g/b/a %d%s/%d/%d/%d/%d srgb %d ac %d/%d/%d/%d/%d dp/stn/ax/b/db/str %u/%u/%u/%s/%s/%s samp %u/%u\n", |
| valid.window ? (pf.window ? "1" : "0") : "?", |
| valid.pbuffer ? (pf.pbuffer ? "1" : "0") : "?", |
| valid.accelerated ? (pf.accelerated ? "1" : "0") : "?", |
| color_bits, |
| float_color == -1 ? "?" : float_color ? "f" : "", |
| red_bits, |
| green_bits, |
| blue_bits, |
| alpha_bits, |
| (int)srgb, |
| accum_bits, |
| accum_red_bits, |
| accum_green_bits, |
| accum_blue_bits, |
| accum_alpha_bits, |
| pf.depth_bits, |
| pf.stencil_bits, |
| pf.aux_buffers, |
| valid.backing_store ? (pf.backing_store ? "1" : "0") : "?", |
| valid.double_buffer ? (pf.double_buffer ? "1" : "0") : "?", |
| valid.stereo ? (pf.stereo ? "1" : "0") : "?", |
| pf.sample_buffers, |
| pf.samples); |
| |
| for (i = 0; i < nb_formats && found < nMaxFormats; i++) |
| { |
| const struct color_mode *mode; |
| |
| if (valid.window && pixel_formats[i].window != pf.window) continue; |
| if (valid.pbuffer && pixel_formats[i].pbuffer != pf.pbuffer) continue; |
| if (valid.accelerated && pixel_formats[i].accelerated != pf.accelerated) continue; |
| if (valid.double_buffer && pixel_formats[i].double_buffer != pf.double_buffer) continue; |
| if (valid.stereo && pixel_formats[i].stereo != pf.stereo) continue; |
| if (valid.backing_store && pixel_formats[i].backing_store != pf.backing_store) continue; |
| |
| if (pixel_formats[i].aux_buffers < pf.aux_buffers) continue; |
| if (pixel_formats[i].depth_bits < pf.depth_bits) continue; |
| if (pixel_formats[i].stencil_bits < pf.stencil_bits) continue; |
| if (pixel_formats[i].sample_buffers < pf.sample_buffers) continue; |
| if (pixel_formats[i].samples < pf.samples) continue; |
| |
| mode = &color_modes[pixel_formats[i].color_mode]; |
| /* If the mode doesn't have alpha, check requested color bits against |
| bits per pixel instead of the mode's color bits. On Windows, color |
| bits sometimes exceeds r+g+b (e.g. it's 32 for an R8G8B8A0 pixel format). |
| If an app depends on that and requests WGL_COLOR_BITS_ARB == 32 and |
| expects that to match such a pixel format, we need to accommodate that. */ |
| if (mode->alpha_bits) |
| { |
| if (mode->color_bits < color_bits) |
| continue; |
| } |
| else |
| { |
| if (mode->bits_per_pixel < color_bits) |
| continue; |
| } |
| if (mode->red_bits < red_bits || mode->green_bits < green_bits || |
| mode->blue_bits < blue_bits || mode->alpha_bits < alpha_bits) |
| continue; |
| if (float_color != -1 && (!mode->is_float != !float_color)) continue; |
| if (srgb && (mode->red_bits != 8 || mode->green_bits != 8 || mode->blue_bits != 8 || mode->is_float)) |
| continue; |
| |
| if (pixel_formats[i].accum_mode) |
| { |
| mode = &color_modes[pixel_formats[i].accum_mode - 1]; |
| if (mode->color_bits < accum_bits || mode->red_bits < accum_red_bits || |
| mode->green_bits < accum_green_bits || mode->blue_bits < accum_blue_bits || |
| mode->alpha_bits < accum_alpha_bits) |
| continue; |
| } |
| else if (accum_bits || accum_red_bits || accum_green_bits || accum_blue_bits || accum_alpha_bits) |
| continue; |
| |
| piFormats[found++] = i + 1; |
| TRACE("match: pixel format %d %s\n", i + 1, debugstr_pf(&pixel_formats[i])); |
| } |
| |
| cant_match: |
| *nNumFormats = found; |
| |
| return TRUE; |
| } |
| |
| |
| /*********************************************************************** |
| * macdrv_wglCreateContextAttribsARB |
| * |
| * WGL_ARB_create_context: wglCreateContextAttribsARB |
| */ |
| static struct wgl_context *macdrv_wglCreateContextAttribsARB(HDC hdc, |
| struct wgl_context *share_context, |
| const int *attrib_list) |
| { |
| int format; |
| struct wgl_context *context; |
| const int *iptr; |
| int major = 1, minor = 0, profile = WGL_CONTEXT_CORE_PROFILE_BIT_ARB, flags = 0; |
| BOOL core = FALSE; |
| |
| TRACE("hdc %p, share_context %p, attrib_list %p\n", hdc, share_context, attrib_list); |
| |
| format = get_dc_pixel_format(hdc); |
| |
| if (!is_valid_pixel_format(format)) |
| { |
| ERR("Invalid pixel format %d, expect problems!\n", format); |
| SetLastError(ERROR_INVALID_PIXEL_FORMAT); |
| return NULL; |
| } |
| |
| for (iptr = attrib_list; iptr && *iptr; iptr += 2) |
| { |
| int attr = iptr[0]; |
| int value = iptr[1]; |
| |
| TRACE("%s\n", debugstr_attrib(attr, value)); |
| |
| switch (attr) |
| { |
| case WGL_CONTEXT_MAJOR_VERSION_ARB: |
| major = value; |
| break; |
| |
| case WGL_CONTEXT_MINOR_VERSION_ARB: |
| minor = value; |
| break; |
| |
| case WGL_CONTEXT_LAYER_PLANE_ARB: |
| WARN("WGL_CONTEXT_LAYER_PLANE_ARB attribute ignored\n"); |
| break; |
| |
| case WGL_CONTEXT_FLAGS_ARB: |
| flags = value; |
| if (flags & ~WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB) |
| WARN("WGL_CONTEXT_FLAGS_ARB attributes %#x ignored\n", |
| flags & ~WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB); |
| break; |
| |
| case WGL_CONTEXT_PROFILE_MASK_ARB: |
| if (value != WGL_CONTEXT_CORE_PROFILE_BIT_ARB && |
| value != WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB) |
| { |
| WARN("WGL_CONTEXT_PROFILE_MASK_ARB bits %#x invalid\n", value); |
| SetLastError(ERROR_INVALID_PROFILE_ARB); |
| return NULL; |
| } |
| profile = value; |
| break; |
| |
| default: |
| WARN("Unknown attribute %s.\n", debugstr_attrib(attr, value)); |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return NULL; |
| } |
| } |
| |
| if ((major == 3 && (minor == 2 || minor == 3)) || |
| (major == 4 && (minor == 0 || minor == 1))) |
| { |
| if (!(flags & WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB)) |
| { |
| WARN("OS X only supports forward-compatible 3.2+ contexts\n"); |
| SetLastError(ERROR_INVALID_VERSION_ARB); |
| return NULL; |
| } |
| if (profile != WGL_CONTEXT_CORE_PROFILE_BIT_ARB) |
| { |
| WARN("Compatibility profiles for GL version >= 3.2 not supported\n"); |
| SetLastError(ERROR_INVALID_PROFILE_ARB); |
| return NULL; |
| } |
| if (major > gl_info.max_major || |
| (major == gl_info.max_major && minor > gl_info.max_minor)) |
| { |
| WARN("This GL implementation does not support the requested GL version %u.%u\n", |
| major, minor); |
| SetLastError(ERROR_INVALID_PROFILE_ARB); |
| return NULL; |
| } |
| core = TRUE; |
| } |
| else if (major >= 3) |
| { |
| WARN("Profile version %u.%u not supported\n", major, minor); |
| SetLastError(ERROR_INVALID_VERSION_ARB); |
| return NULL; |
| } |
| else if (major < 1 || (major == 1 && (minor < 0 || minor > 5)) || |
| (major == 2 && (minor < 0 || minor > 1))) |
| { |
| WARN("Invalid GL version requested\n"); |
| SetLastError(ERROR_INVALID_VERSION_ARB); |
| return NULL; |
| } |
| if (!core && flags & WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB) |
| { |
| WARN("Forward compatible context requested for GL version < 3\n"); |
| SetLastError(ERROR_INVALID_VERSION_ARB); |
| return NULL; |
| } |
| |
| if (!(context = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*context)))) return NULL; |
| |
| context->format = format; |
| if (!create_context(context, share_context ? share_context->cglcontext : NULL, major)) |
| { |
| HeapFree(GetProcessHeap(), 0, context); |
| return NULL; |
| } |
| |
| EnterCriticalSection(&context_section); |
| list_add_tail(&context_list, &context->entry); |
| LeaveCriticalSection(&context_section); |
| |
| return context; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglCreatePbufferARB |
| * |
| * WGL_ARB_pbuffer: wglCreatePbufferARB |
| */ |
| static struct wgl_pbuffer *macdrv_wglCreatePbufferARB(HDC hdc, int iPixelFormat, int iWidth, int iHeight, |
| const int *piAttribList) |
| { |
| struct wgl_pbuffer* pbuffer; |
| GLenum target = 0; |
| GLenum internalFormat = 0; |
| CGLError err; |
| |
| TRACE("hdc %p iPixelFormat %d iWidth %d iHeight %d piAttribList %p\n", |
| hdc, iPixelFormat, iWidth, iHeight, piAttribList); |
| |
| if (!is_valid_pixel_format(iPixelFormat) || !pixel_formats[iPixelFormat - 1].pbuffer) |
| { |
| WARN("invalid pixel format %d\n", iPixelFormat); |
| SetLastError(ERROR_INVALID_PIXEL_FORMAT); |
| return NULL; |
| } |
| |
| pbuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pbuffer)); |
| pbuffer->format = iPixelFormat; |
| |
| for ( ; piAttribList && *piAttribList; piAttribList += 2) |
| { |
| int attr = piAttribList[0]; |
| int value = piAttribList[1]; |
| |
| switch (attr) |
| { |
| case WGL_PBUFFER_LARGEST_ARB: |
| FIXME("WGL_PBUFFER_LARGEST_ARB: %d; ignoring\n", value); |
| break; |
| |
| case WGL_TEXTURE_FORMAT_ARB: |
| switch (value) |
| { |
| case WGL_TEXTURE_RGBA_ARB: |
| TRACE("WGL_TEXTURE_FORMAT_ARB: WGL_TEXTURE_RGBA_ARB\n"); |
| internalFormat = GL_RGBA; |
| break; |
| case WGL_TEXTURE_RGB_ARB: |
| TRACE("WGL_TEXTURE_FORMAT_ARB: WGL_TEXTURE_RGB_ARB\n"); |
| internalFormat = GL_RGB; |
| break; |
| case WGL_NO_TEXTURE_ARB: |
| TRACE("WGL_TEXTURE_FORMAT_ARB: WGL_NO_TEXTURE_ARB\n"); |
| internalFormat = 0; |
| break; |
| default: |
| WARN("unknown WGL_TEXTURE_FORMAT_ARB value 0x%x\n", value); |
| SetLastError(ERROR_INVALID_DATA); |
| goto done; |
| } |
| break; |
| |
| case WGL_TEXTURE_TARGET_ARB: |
| pbuffer->face = 0; |
| switch (value) |
| { |
| case WGL_NO_TEXTURE_ARB: |
| TRACE("WGL_TEXTURE_TARGET_ARB: WGL_NO_TEXTURE_ARB\n"); |
| target = 0; |
| break; |
| case WGL_TEXTURE_CUBE_MAP_ARB: |
| TRACE("WGL_TEXTURE_TARGET_ARB: WGL_TEXTURE_CUBE_MAP_ARB\n"); |
| target = GL_TEXTURE_CUBE_MAP; |
| pbuffer->face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; |
| break; |
| case WGL_TEXTURE_1D_ARB: |
| FIXME("WGL_TEXTURE_TARGET_ARB: WGL_TEXTURE_1D_ARB; not supported\n"); |
| SetLastError(ERROR_NO_SYSTEM_RESOURCES); |
| goto done; |
| case WGL_TEXTURE_2D_ARB: |
| TRACE("WGL_TEXTURE_TARGET_ARB: WGL_TEXTURE_2D_ARB\n"); |
| target = GL_TEXTURE_2D; |
| break; |
| case WGL_TEXTURE_RECTANGLE_NV: |
| TRACE("WGL_TEXTURE_TARGET_ARB: WGL_TEXTURE_RECTANGLE_NV\n"); |
| target = GL_TEXTURE_RECTANGLE; |
| break; |
| default: |
| WARN("unknown WGL_TEXTURE_TARGET_ARB value 0x%x\n", value); |
| SetLastError(ERROR_INVALID_DATA); |
| goto done; |
| } |
| break; |
| |
| case WGL_MIPMAP_TEXTURE_ARB: |
| TRACE("WGL_MIPMAP_TEXTURE_ARB: %d\n", value); |
| pbuffer->max_level = 0; |
| if (value) |
| { |
| int size = min(iWidth, iHeight) / 2; |
| while (size) |
| { |
| pbuffer->max_level++; |
| size /= 2; |
| } |
| } |
| break; |
| |
| default: |
| WARN("unknown attribute 0x%x\n", attr); |
| SetLastError(ERROR_INVALID_DATA); |
| goto done; |
| } |
| } |
| |
| if (!target || !internalFormat) |
| { |
| pbuffer->no_texture = TRUE; |
| /* no actual way to turn off ability to texture; use most permissive target */ |
| target = GL_TEXTURE_RECTANGLE; |
| internalFormat = GL_RGB; |
| } |
| |
| err = CGLCreatePBuffer(iWidth, iHeight, target, internalFormat, pbuffer->max_level, &pbuffer->pbuffer); |
| if (err != kCGLNoError) |
| { |
| WARN("CGLCreatePBuffer failed; err %d %s\n", err, CGLErrorString(err)); |
| pbuffer->pbuffer = NULL; |
| if (err == kCGLBadAlloc) |
| SetLastError(ERROR_NO_SYSTEM_RESOURCES); |
| else |
| SetLastError(ERROR_INVALID_DATA); |
| } |
| |
| done: |
| if (!pbuffer->pbuffer) |
| { |
| HeapFree(GetProcessHeap(), 0, pbuffer); |
| return NULL; |
| } |
| |
| TRACE(" -> %p\n", pbuffer); |
| return pbuffer; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglDestroyPbufferARB |
| * |
| * WGL_ARB_pbuffer: wglDestroyPbufferARB |
| */ |
| static BOOL macdrv_wglDestroyPbufferARB(struct wgl_pbuffer *pbuffer) |
| { |
| TRACE("pbuffer %p\n", pbuffer); |
| if (pbuffer && pbuffer->pbuffer) |
| CGLReleasePBuffer(pbuffer->pbuffer); |
| HeapFree(GetProcessHeap(), 0, pbuffer); |
| return GL_TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglGetExtensionsStringARB |
| * |
| * WGL_ARB_extensions_string: wglGetExtensionsStringARB |
| */ |
| static const char *macdrv_wglGetExtensionsStringARB(HDC hdc) |
| { |
| /* FIXME: Since we're given an HDC, this should be device-specific. I.e. |
| this can be specific to the CGL renderer like we're supposed to do. */ |
| TRACE("returning \"%s\"\n", gl_info.wglExtensions); |
| return gl_info.wglExtensions; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglGetExtensionsStringEXT |
| * |
| * WGL_EXT_extensions_string: wglGetExtensionsStringEXT |
| */ |
| static const char *macdrv_wglGetExtensionsStringEXT(void) |
| { |
| TRACE("returning \"%s\"\n", gl_info.wglExtensions); |
| return gl_info.wglExtensions; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglGetPbufferDCARB |
| * |
| * WGL_ARB_pbuffer: wglGetPbufferDCARB |
| */ |
| static HDC macdrv_wglGetPbufferDCARB(struct wgl_pbuffer *pbuffer) |
| { |
| HDC hdc; |
| struct wgl_pbuffer *prev; |
| |
| hdc = CreateDCA("DISPLAY", NULL, NULL, NULL); |
| if (!hdc) return 0; |
| |
| EnterCriticalSection(&dc_pbuffers_section); |
| prev = (struct wgl_pbuffer*)CFDictionaryGetValue(dc_pbuffers, hdc); |
| if (prev) |
| { |
| CGLReleasePBuffer(prev->pbuffer); |
| HeapFree(GetProcessHeap(), 0, prev); |
| } |
| CFDictionarySetValue(dc_pbuffers, hdc, pbuffer); |
| LeaveCriticalSection(&dc_pbuffers_section); |
| |
| TRACE("pbuffer %p -> hdc %p\n", pbuffer, hdc); |
| return hdc; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglGetPixelFormatAttribivARB |
| * |
| * WGL_ARB_pixel_format: wglGetPixelFormatAttribivARB |
| */ |
| static BOOL macdrv_wglGetPixelFormatAttribivARB(HDC hdc, int iPixelFormat, int iLayerPlane, |
| UINT nAttributes, const int *piAttributes, int *piValues) |
| { |
| const pixel_format *pf; |
| UINT i; |
| |
| TRACE("hdc %p iPixelFormat %d iLayerPlane %d nAttributes %u piAttributes %p piValues %p\n", |
| hdc, iPixelFormat, iLayerPlane, nAttributes, piAttributes, piValues); |
| |
| if (!nAttributes) return GL_TRUE; |
| |
| if (nAttributes == 1 && piAttributes[0] == WGL_NUMBER_PIXEL_FORMATS_ARB) |
| { |
| piValues[0] = nb_formats; |
| TRACE("%s\n", debugstr_attrib(piAttributes[0], piValues[0])); |
| return GL_TRUE; |
| } |
| |
| pf = get_pixel_format(iPixelFormat, TRUE /* non-displayable */); |
| if (!pf) |
| { |
| WARN("invalid pixel format %d\n", iPixelFormat); |
| SetLastError(ERROR_INVALID_PIXEL_FORMAT); |
| return GL_FALSE; |
| } |
| |
| for (i = 0; i < nAttributes; ++i) |
| { |
| switch (piAttributes[i]) |
| { |
| case WGL_NUMBER_PIXEL_FORMATS_ARB: |
| piValues[i] = nb_formats; |
| break; |
| |
| case WGL_DRAW_TO_WINDOW_ARB: |
| piValues[i] = pf->window ? GL_TRUE : GL_FALSE; |
| break; |
| |
| case WGL_DRAW_TO_BITMAP_ARB: |
| piValues[i] = GL_FALSE; |
| break; |
| |
| case WGL_ACCELERATION_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| if (pf->accelerated) |
| piValues[i] = WGL_FULL_ACCELERATION_ARB; |
| else |
| piValues[i] = WGL_NO_ACCELERATION_ARB; |
| break; |
| |
| case WGL_NEED_PALETTE_ARB: |
| case WGL_NEED_SYSTEM_PALETTE_ARB: |
| case WGL_SWAP_LAYER_BUFFERS_ARB: |
| piValues[i] = GL_FALSE; |
| break; |
| |
| case WGL_SWAP_METHOD_ARB: |
| if (pf->double_buffer && pf->backing_store) |
| piValues[i] = WGL_SWAP_COPY_ARB; |
| else |
| piValues[i] = WGL_SWAP_UNDEFINED_ARB; |
| break; |
| |
| case WGL_NUMBER_OVERLAYS_ARB: |
| case WGL_NUMBER_UNDERLAYS_ARB: |
| piValues[i] = 0; |
| break; |
| |
| case WGL_TRANSPARENT_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = GL_FALSE; |
| break; |
| |
| case WGL_TRANSPARENT_RED_VALUE_ARB: |
| case WGL_TRANSPARENT_GREEN_VALUE_ARB: |
| case WGL_TRANSPARENT_BLUE_VALUE_ARB: |
| case WGL_TRANSPARENT_ALPHA_VALUE_ARB: |
| case WGL_TRANSPARENT_INDEX_VALUE_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = 0; |
| break; |
| |
| case WGL_SHARE_DEPTH_ARB: |
| case WGL_SHARE_STENCIL_ARB: |
| case WGL_SHARE_ACCUM_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = GL_TRUE; |
| break; |
| |
| case WGL_SUPPORT_GDI_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = GL_FALSE; |
| break; |
| |
| case WGL_SUPPORT_OPENGL_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = GL_TRUE; |
| break; |
| |
| case WGL_DOUBLE_BUFFER_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = pf->double_buffer ? GL_TRUE : GL_FALSE; |
| break; |
| |
| case WGL_STEREO_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = pf->stereo ? GL_TRUE : GL_FALSE; |
| break; |
| |
| case WGL_PIXEL_TYPE_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| if (color_modes[pf->color_mode].is_float) |
| piValues[i] = WGL_TYPE_RGBA_FLOAT_ARB; |
| else |
| piValues[i] = WGL_TYPE_RGBA_ARB; |
| /* WGL_EXT_pixel_format_packed_float may be supported, which should in theory |
| make another pixel type available: WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT. |
| However, Mac contexts don't support rendering to unsigned floating-point |
| formats, even when GL_EXT_packed_float is supported. */ |
| break; |
| |
| case WGL_COLOR_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| /* If the mode doesn't have alpha, return bits per pixel instead |
| of color bits. On Windows, color bits sometimes exceeds r+g+b |
| (e.g. it's 32 for an R8G8B8A0 pixel format). If an app depends |
| on that and expects that WGL_COLOR_BITS_ARB >= 32 for such a |
| pixel format, we need to accommodate that. */ |
| if (color_modes[pf->color_mode].alpha_bits) |
| piValues[i] = color_modes[pf->color_mode].color_bits; |
| else |
| piValues[i] = color_modes[pf->color_mode].bits_per_pixel; |
| break; |
| |
| case WGL_RED_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = color_modes[pf->color_mode].red_bits; |
| break; |
| |
| case WGL_RED_SHIFT_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = color_modes[pf->color_mode].red_shift; |
| break; |
| |
| case WGL_GREEN_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = color_modes[pf->color_mode].green_bits; |
| break; |
| |
| case WGL_GREEN_SHIFT_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = color_modes[pf->color_mode].green_shift; |
| break; |
| |
| case WGL_BLUE_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = color_modes[pf->color_mode].blue_bits; |
| break; |
| |
| case WGL_BLUE_SHIFT_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = color_modes[pf->color_mode].blue_shift; |
| break; |
| |
| case WGL_ALPHA_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = color_modes[pf->color_mode].alpha_bits; |
| break; |
| |
| case WGL_ALPHA_SHIFT_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = color_modes[pf->color_mode].alpha_shift; |
| break; |
| |
| case WGL_ACCUM_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| if (pf->accum_mode) |
| piValues[i] = color_modes[pf->accum_mode - 1].color_bits; |
| else |
| piValues[i] = 0; |
| break; |
| |
| case WGL_ACCUM_RED_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| if (pf->accum_mode) |
| piValues[i] = color_modes[pf->accum_mode - 1].red_bits; |
| else |
| piValues[i] = 0; |
| break; |
| |
| case WGL_ACCUM_GREEN_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| if (pf->accum_mode) |
| piValues[i] = color_modes[pf->accum_mode - 1].green_bits; |
| else |
| piValues[i] = 0; |
| break; |
| |
| case WGL_ACCUM_BLUE_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| if (pf->accum_mode) |
| piValues[i] = color_modes[pf->accum_mode - 1].blue_bits; |
| else |
| piValues[i] = 0; |
| break; |
| |
| case WGL_ACCUM_ALPHA_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| if (pf->accum_mode) |
| piValues[i] = color_modes[pf->accum_mode - 1].alpha_bits; |
| else |
| piValues[i] = 0; |
| break; |
| |
| case WGL_DEPTH_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = pf->depth_bits; |
| break; |
| |
| case WGL_STENCIL_BITS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = pf->stencil_bits; |
| break; |
| |
| case WGL_AUX_BUFFERS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = pf->aux_buffers; |
| break; |
| |
| case WGL_SAMPLE_BUFFERS_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = pf->sample_buffers; |
| break; |
| |
| case WGL_SAMPLES_ARB: |
| if (iLayerPlane) goto invalid_layer; |
| piValues[i] = pf->samples; |
| break; |
| |
| case WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB: /* a.k.a. WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT */ |
| if (iLayerPlane) goto invalid_layer; |
| /* sRGB is only supported for 8-bit integer color components */ |
| if (color_modes[pf->color_mode].red_bits == 8 && |
| color_modes[pf->color_mode].green_bits == 8 && |
| color_modes[pf->color_mode].blue_bits == 8 && |
| !color_modes[pf->color_mode].is_float) |
| piValues[i] = GL_TRUE; |
| else |
| piValues[i] = GL_FALSE; |
| break; |
| |
| case WGL_DRAW_TO_PBUFFER_ARB: |
| case WGL_BIND_TO_TEXTURE_RGB_ARB: |
| case WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV: |
| piValues[i] = pf->pbuffer ? GL_TRUE : GL_FALSE; |
| break; |
| |
| case WGL_BIND_TO_TEXTURE_RGBA_ARB: |
| case WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV: |
| piValues[i] = (pf->pbuffer && color_modes[pf->color_mode].alpha_bits) ? GL_TRUE : GL_FALSE; |
| break; |
| |
| case WGL_MAX_PBUFFER_WIDTH_ARB: |
| piValues[i] = gl_info.max_viewport_dims[0]; |
| break; |
| |
| case WGL_MAX_PBUFFER_HEIGHT_ARB: |
| piValues[i] = gl_info.max_viewport_dims[1]; |
| break; |
| |
| case WGL_MAX_PBUFFER_PIXELS_ARB: |
| piValues[i] = gl_info.max_viewport_dims[0] * gl_info.max_viewport_dims[1]; |
| break; |
| |
| default: |
| WARN("invalid attribute %x\n", piAttributes[i]); |
| return GL_FALSE; |
| } |
| |
| TRACE("%s\n", debugstr_attrib(piAttributes[i], piValues[i])); |
| } |
| |
| return GL_TRUE; |
| |
| invalid_layer: |
| FIXME("unsupported iLayerPlane %d\n", iLayerPlane); |
| return GL_FALSE; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglGetPixelFormatAttribfvARB |
| * |
| * WGL_ARB_pixel_format: wglGetPixelFormatAttribfvARB |
| */ |
| static BOOL macdrv_wglGetPixelFormatAttribfvARB(HDC hdc, int iPixelFormat, int iLayerPlane, |
| UINT nAttributes, const int *piAttributes, FLOAT *pfValues) |
| { |
| int *attr; |
| int ret; |
| |
| TRACE("hdc %p iPixelFormat %d iLayerPlane %d nAttributes %u piAttributes %p pfValues %p\n", |
| hdc, iPixelFormat, iLayerPlane, nAttributes, piAttributes, pfValues); |
| |
| /* Allocate a temporary array to store integer values */ |
| attr = HeapAlloc(GetProcessHeap(), 0, nAttributes * sizeof(int)); |
| if (!attr) |
| { |
| ERR("couldn't allocate %d array\n", nAttributes); |
| return GL_FALSE; |
| } |
| |
| /* Piggy-back on wglGetPixelFormatAttribivARB */ |
| ret = macdrv_wglGetPixelFormatAttribivARB(hdc, iPixelFormat, iLayerPlane, nAttributes, piAttributes, attr); |
| if (ret) |
| { |
| UINT i; |
| |
| /* Convert integer values to float. Should also check for attributes |
| that can give decimal values here */ |
| for (i = 0; i < nAttributes; i++) |
| pfValues[i] = attr[i]; |
| } |
| |
| HeapFree(GetProcessHeap(), 0, attr); |
| return ret; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglGetSwapIntervalEXT |
| * |
| * WGL_EXT_swap_control: wglGetSwapIntervalEXT |
| */ |
| static int macdrv_wglGetSwapIntervalEXT(void) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| struct macdrv_win_data *data; |
| long value; |
| CGLError err; |
| |
| TRACE("\n"); |
| |
| if ((data = get_win_data(context->draw_hwnd))) |
| { |
| value = data->swap_interval; |
| release_win_data(data); |
| |
| if (InterlockedCompareExchange(&context->update_swap_interval, FALSE, TRUE)) |
| set_swap_interval(context, value); |
| } |
| else |
| { |
| err = CGLGetParameter(context->cglcontext, kCGLCPSwapInterval, (GLint*)&value); |
| if (err != kCGLNoError) |
| { |
| WARN("CGLGetParameter(kCGLCPSwapInterval) failed; error %d %s\n", |
| err, CGLErrorString(err)); |
| value = 1; |
| } |
| } |
| |
| return value; |
| } |
| |
| |
| /*********************************************************************** |
| * macdrv_wglMakeContextCurrentARB |
| * |
| * WGL_ARB_make_current_read: wglMakeContextCurrentARB |
| * |
| * This is not supported directly by OpenGL on the Mac. We emulate it |
| * by hooking into glReadPixels, glCopyPixels, and glCopyColorTable to |
| * temporarily swap the drawable. This follows the technique used in |
| * the implementation of Mesa GLX for Apple. |
| */ |
| static BOOL macdrv_wglMakeContextCurrentARB(HDC draw_hdc, HDC read_hdc, struct wgl_context *context) |
| { |
| struct macdrv_win_data *data; |
| HWND hwnd; |
| |
| TRACE("draw_hdc %p read_hdc %p context %p/%p/%p\n", draw_hdc, read_hdc, context, |
| (context ? context->context : NULL), (context ? context->cglcontext : NULL)); |
| |
| if (!context) |
| { |
| macdrv_make_context_current(NULL, NULL); |
| NtCurrentTeb()->glContext = NULL; |
| return TRUE; |
| } |
| |
| if ((hwnd = WindowFromDC(draw_hdc))) |
| { |
| if (!(data = get_win_data(hwnd))) |
| { |
| FIXME("draw DC for window %p of other process: not implemented\n", hwnd); |
| return FALSE; |
| } |
| |
| if (!data->pixel_format) |
| { |
| WARN("no pixel format set\n"); |
| release_win_data(data); |
| SetLastError(ERROR_INVALID_HANDLE); |
| return FALSE; |
| } |
| if (context->format != data->pixel_format) |
| { |
| WARN("mismatched pixel format draw_hdc %p %u context %p %u\n", draw_hdc, data->pixel_format, context, context->format); |
| release_win_data(data); |
| SetLastError(ERROR_INVALID_PIXEL_FORMAT); |
| return FALSE; |
| } |
| |
| if (allow_vsync && (InterlockedCompareExchange(&context->update_swap_interval, FALSE, TRUE) || hwnd != context->draw_hwnd)) |
| set_swap_interval(context, data->swap_interval); |
| |
| context->draw_hwnd = hwnd; |
| context->draw_view = data->gl_view; |
| context->draw_pbuffer = NULL; |
| release_win_data(data); |
| } |
| else |
| { |
| struct wgl_pbuffer *pbuffer; |
| |
| EnterCriticalSection(&dc_pbuffers_section); |
| pbuffer = (struct wgl_pbuffer*)CFDictionaryGetValue(dc_pbuffers, draw_hdc); |
| if (pbuffer) |
| { |
| if (context->format != pbuffer->format) |
| { |
| WARN("mismatched pixel format draw_hdc %p %u context %p %u\n", draw_hdc, pbuffer->format, context, context->format); |
| LeaveCriticalSection(&dc_pbuffers_section); |
| SetLastError(ERROR_INVALID_PIXEL_FORMAT); |
| return FALSE; |
| } |
| |
| if (allow_vsync && |
| (InterlockedCompareExchange(&context->update_swap_interval, FALSE, TRUE) || pbuffer != context->draw_pbuffer)) |
| set_swap_interval(context, 0); |
| } |
| else |
| { |
| WARN("no window or pbuffer for DC\n"); |
| LeaveCriticalSection(&dc_pbuffers_section); |
| SetLastError(ERROR_INVALID_HANDLE); |
| return FALSE; |
| } |
| |
| context->draw_hwnd = NULL; |
| context->draw_view = NULL; |
| context->draw_pbuffer = pbuffer; |
| LeaveCriticalSection(&dc_pbuffers_section); |
| } |
| |
| context->read_view = NULL; |
| context->read_pbuffer = NULL; |
| if (read_hdc && read_hdc != draw_hdc) |
| { |
| if ((hwnd = WindowFromDC(read_hdc))) |
| { |
| if ((data = get_win_data(hwnd))) |
| { |
| if (data->gl_view != context->draw_view) |
| context->read_view = data->gl_view; |
| release_win_data(data); |
| } |
| } |
| else |
| { |
| EnterCriticalSection(&dc_pbuffers_section); |
| context->read_pbuffer = (struct wgl_pbuffer*)CFDictionaryGetValue(dc_pbuffers, read_hdc); |
| LeaveCriticalSection(&dc_pbuffers_section); |
| } |
| } |
| |
| TRACE("making context current with draw_view %p draw_pbuffer %p read_view %p read_pbuffer %p format %u\n", |
| context->draw_view, context->draw_pbuffer, context->read_view, context->read_pbuffer, context->format); |
| |
| make_context_current(context, FALSE); |
| context->has_been_current = TRUE; |
| NtCurrentTeb()->glContext = context; |
| |
| return TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglQueryPbufferARB |
| * |
| * WGL_ARB_pbuffer: wglQueryPbufferARB |
| */ |
| static BOOL macdrv_wglQueryPbufferARB(struct wgl_pbuffer *pbuffer, int iAttribute, int *piValue) |
| { |
| CGLError err; |
| GLsizei width; |
| GLsizei height; |
| GLenum target; |
| GLenum internalFormat; |
| GLint mipmap; |
| |
| TRACE("pbuffer %p iAttribute 0x%x piValue %p\n", pbuffer, iAttribute, piValue); |
| |
| err = CGLDescribePBuffer(pbuffer->pbuffer, &width, &height, &target, &internalFormat, &mipmap); |
| if (err != kCGLNoError) |
| { |
| WARN("CGLDescribePBuffer failed; error %d %s\n", err, CGLErrorString(err)); |
| SetLastError(ERROR_INVALID_HANDLE); |
| return GL_FALSE; |
| } |
| |
| switch (iAttribute) |
| { |
| case WGL_PBUFFER_WIDTH_ARB: |
| *piValue = width; |
| break; |
| case WGL_PBUFFER_HEIGHT_ARB: |
| *piValue = height; |
| break; |
| case WGL_PBUFFER_LOST_ARB: |
| /* Mac PBuffers can't be lost */ |
| *piValue = GL_FALSE; |
| break; |
| case WGL_TEXTURE_FORMAT_ARB: |
| if (pbuffer->no_texture) |
| *piValue = WGL_NO_TEXTURE_ARB; |
| else switch (internalFormat) |
| { |
| case GL_RGBA: |
| *piValue = WGL_TEXTURE_RGBA_ARB; |
| break; |
| case GL_RGB: |
| default: |
| *piValue = WGL_TEXTURE_RGB_ARB; |
| break; |
| } |
| break; |
| case WGL_TEXTURE_TARGET_ARB: |
| if (pbuffer->no_texture) |
| *piValue = WGL_NO_TEXTURE_ARB; |
| else switch (target) |
| { |
| case GL_TEXTURE_CUBE_MAP: |
| *piValue = WGL_TEXTURE_CUBE_MAP_ARB; |
| break; |
| case GL_TEXTURE_2D: |
| *piValue = WGL_TEXTURE_2D_ARB; |
| break; |
| case GL_TEXTURE_RECTANGLE: |
| default: |
| *piValue = WGL_TEXTURE_RECTANGLE_NV; |
| break; |
| } |
| break; |
| case WGL_MIPMAP_TEXTURE_ARB: |
| *piValue = (pbuffer->max_level > 0); |
| break; |
| case WGL_MIPMAP_LEVEL_ARB: |
| *piValue = pbuffer->level; |
| break; |
| case WGL_CUBE_MAP_FACE_ARB: |
| switch (pbuffer->face) |
| { |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_X: |
| default: |
| *piValue = WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB; |
| break; |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: |
| *piValue = WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB; |
| break; |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: |
| *piValue = WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB; |
| break; |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: |
| *piValue = WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB; |
| break; |
| case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: |
| *piValue = WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB; |
| break; |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: |
| *piValue = WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB; |
| break; |
| } |
| break; |
| default: |
| WARN("invalid attribute 0x%x\n", iAttribute); |
| SetLastError(ERROR_INVALID_DATA); |
| return GL_FALSE; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglReleasePbufferDCARB |
| * |
| * WGL_ARB_pbuffer: wglReleasePbufferDCARB |
| */ |
| static int macdrv_wglReleasePbufferDCARB(struct wgl_pbuffer *pbuffer, HDC hdc) |
| { |
| struct wgl_pbuffer *prev; |
| |
| TRACE("pbuffer %p hdc %p\n", pbuffer, hdc); |
| |
| EnterCriticalSection(&dc_pbuffers_section); |
| |
| prev = (struct wgl_pbuffer*)CFDictionaryGetValue(dc_pbuffers, hdc); |
| if (prev) |
| { |
| if (prev != pbuffer) |
| FIXME("hdc %p isn't associated with pbuffer %p\n", hdc, pbuffer); |
| CGLReleasePBuffer(prev->pbuffer); |
| HeapFree(GetProcessHeap(), 0, prev); |
| CFDictionaryRemoveValue(dc_pbuffers, hdc); |
| } |
| else hdc = 0; |
| |
| LeaveCriticalSection(&dc_pbuffers_section); |
| |
| return hdc && DeleteDC(hdc); |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglReleaseTexImageARB |
| * |
| * WGL_ARB_render_texture: wglReleaseTexImageARB |
| */ |
| static BOOL macdrv_wglReleaseTexImageARB(struct wgl_pbuffer *pbuffer, int iBuffer) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| CGLError err; |
| |
| TRACE("pbuffer %p iBuffer 0x%x; stub!\n", pbuffer, iBuffer); |
| |
| if (pbuffer->no_texture) |
| { |
| SetLastError(ERROR_INVALID_OPERATION); |
| return GL_FALSE; |
| } |
| |
| err = CGLTexImagePBuffer(context->cglcontext, pbuffer->pbuffer, GL_NONE); |
| if (err != kCGLNoError) |
| { |
| WARN("CGLTexImagePBuffer failed with err %d %s\n", err, CGLErrorString(err)); |
| SetLastError(ERROR_INVALID_OPERATION); |
| return GL_FALSE; |
| } |
| |
| return GL_TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglSetPbufferAttribARB |
| * |
| * WGL_ARB_render_texture: wglSetPbufferAttribARB |
| */ |
| static BOOL macdrv_wglSetPbufferAttribARB(struct wgl_pbuffer *pbuffer, const int *piAttribList) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| |
| TRACE("pbuffer %p piAttribList %p\n", pbuffer, piAttribList); |
| |
| for ( ; piAttribList && *piAttribList; piAttribList += 2) |
| { |
| int attr = piAttribList[0]; |
| int value = piAttribList[1]; |
| switch (attr) |
| { |
| case WGL_MIPMAP_LEVEL_ARB: |
| TRACE("WGL_MIPMAP_LEVEL_ARB: %d\n", value); |
| pbuffer->level = value; |
| break; |
| case WGL_CUBE_MAP_FACE_ARB: |
| switch (value) |
| { |
| case WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB: |
| TRACE("WGL_CUBE_MAP_FACE_ARB: WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB\n"); |
| pbuffer->face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; |
| break; |
| case WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB: |
| TRACE("WGL_CUBE_MAP_FACE_ARB: WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB\n"); |
| pbuffer->face = GL_TEXTURE_CUBE_MAP_NEGATIVE_X; |
| break; |
| case WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB: |
| TRACE("WGL_CUBE_MAP_FACE_ARB: WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB\n"); |
| pbuffer->face = GL_TEXTURE_CUBE_MAP_POSITIVE_Y; |
| break; |
| case WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB: |
| TRACE("WGL_CUBE_MAP_FACE_ARB: WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB\n"); |
| pbuffer->face = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; |
| break; |
| case WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB: |
| TRACE("WGL_CUBE_MAP_FACE_ARB: WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB\n"); |
| pbuffer->face = GL_TEXTURE_CUBE_MAP_POSITIVE_Z; |
| break; |
| case WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB: |
| TRACE("WGL_CUBE_MAP_FACE_ARB: WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB\n"); |
| pbuffer->face = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; |
| break; |
| default: |
| WARN("unknown WGL_CUBE_MAP_FACE_ARB value 0x%x\n", value); |
| SetLastError(ERROR_INVALID_DATA); |
| return GL_FALSE; |
| } |
| break; |
| default: |
| WARN("invalide attribute 0x%x\n", attr); |
| SetLastError(ERROR_INVALID_DATA); |
| return GL_FALSE; |
| } |
| } |
| |
| if (context && context->draw_pbuffer == pbuffer) |
| make_context_current(context, FALSE); |
| |
| return GL_TRUE; |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglSetPixelFormatWINE |
| * |
| * WGL_WINE_pixel_format_passthrough: wglSetPixelFormatWINE |
| */ |
| static BOOL macdrv_wglSetPixelFormatWINE(HDC hdc, int fmt) |
| { |
| return set_pixel_format(hdc, fmt, TRUE); |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglSwapIntervalEXT |
| * |
| * WGL_EXT_swap_control: wglSwapIntervalEXT |
| */ |
| static BOOL macdrv_wglSwapIntervalEXT(int interval) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| BOOL changed = FALSE; |
| |
| TRACE("interval %d\n", interval); |
| |
| if (interval < 0) |
| { |
| SetLastError(ERROR_INVALID_DATA); |
| return FALSE; |
| } |
| if (interval > 1) |
| interval = 1; |
| |
| if (context->draw_hwnd) |
| { |
| struct macdrv_win_data *data = get_win_data(context->draw_hwnd); |
| if (data) |
| { |
| changed = data->swap_interval != interval; |
| if (changed) |
| data->swap_interval = interval; |
| release_win_data(data); |
| } |
| } |
| else /* pbuffer */ |
| interval = 0; |
| |
| InterlockedExchange(&context->update_swap_interval, FALSE); |
| if (!set_swap_interval(context, interval)) |
| { |
| SetLastError(ERROR_GEN_FAILURE); |
| return FALSE; |
| } |
| |
| if (changed) |
| { |
| struct wgl_context *ctx; |
| |
| EnterCriticalSection(&context_section); |
| LIST_FOR_EACH_ENTRY(ctx, &context_list, struct wgl_context, entry) |
| { |
| if (ctx != context && ctx->draw_hwnd == context->draw_hwnd) |
| InterlockedExchange(&context->update_swap_interval, TRUE); |
| } |
| LeaveCriticalSection(&context_section); |
| } |
| |
| return TRUE; |
| } |
| |
| |
| static void register_extension(const char *ext) |
| { |
| if (gl_info.wglExtensions[0]) |
| strcat(gl_info.wglExtensions, " "); |
| strcat(gl_info.wglExtensions, ext); |
| |
| TRACE("'%s'\n", ext); |
| } |
| |
| static void load_extensions(void) |
| { |
| /* |
| * ARB Extensions |
| */ |
| register_extension("WGL_ARB_extensions_string"); |
| opengl_funcs.ext.p_wglGetExtensionsStringARB = macdrv_wglGetExtensionsStringARB; |
| |
| register_extension("WGL_ARB_make_current_read"); |
| opengl_funcs.ext.p_wglGetCurrentReadDCARB = (void *)1; /* never called */ |
| opengl_funcs.ext.p_wglMakeContextCurrentARB = macdrv_wglMakeContextCurrentARB; |
| |
| register_extension("WGL_ARB_pixel_format"); |
| opengl_funcs.ext.p_wglChoosePixelFormatARB = macdrv_wglChoosePixelFormatARB; |
| opengl_funcs.ext.p_wglGetPixelFormatAttribfvARB = macdrv_wglGetPixelFormatAttribfvARB; |
| opengl_funcs.ext.p_wglGetPixelFormatAttribivARB = macdrv_wglGetPixelFormatAttribivARB; |
| |
| if (gluCheckExtension((GLubyte*)"GL_ARB_color_buffer_float", (GLubyte*)gl_info.glExtensions)) |
| { |
| register_extension("WGL_ARB_pixel_format_float"); |
| register_extension("WGL_ATI_pixel_format_float"); |
| } |
| |
| if (gluCheckExtension((GLubyte*)"GL_ARB_multisample", (GLubyte*)gl_info.glExtensions)) |
| register_extension("WGL_ARB_multisample"); |
| |
| if (gluCheckExtension((GLubyte*)"GL_ARB_framebuffer_sRGB", (GLubyte*)gl_info.glExtensions)) |
| register_extension("WGL_ARB_framebuffer_sRGB"); |
| |
| if (gluCheckExtension((GLubyte*)"GL_APPLE_pixel_buffer", (GLubyte*)gl_info.glExtensions)) |
| { |
| register_extension("WGL_ARB_pbuffer"); |
| opengl_funcs.ext.p_wglCreatePbufferARB = macdrv_wglCreatePbufferARB; |
| opengl_funcs.ext.p_wglDestroyPbufferARB = macdrv_wglDestroyPbufferARB; |
| opengl_funcs.ext.p_wglGetPbufferDCARB = macdrv_wglGetPbufferDCARB; |
| opengl_funcs.ext.p_wglQueryPbufferARB = macdrv_wglQueryPbufferARB; |
| opengl_funcs.ext.p_wglReleasePbufferDCARB = macdrv_wglReleasePbufferDCARB; |
| |
| register_extension("WGL_ARB_render_texture"); |
| opengl_funcs.ext.p_wglBindTexImageARB = macdrv_wglBindTexImageARB; |
| opengl_funcs.ext.p_wglReleaseTexImageARB = macdrv_wglReleaseTexImageARB; |
| opengl_funcs.ext.p_wglSetPbufferAttribARB = macdrv_wglSetPbufferAttribARB; |
| |
| if (gluCheckExtension((GLubyte*)"GL_ARB_texture_rectangle", (GLubyte*)gl_info.glExtensions) || |
| gluCheckExtension((GLubyte*)"GL_EXT_texture_rectangle", (GLubyte*)gl_info.glExtensions)) |
| register_extension("WGL_NV_render_texture_rectangle"); |
| } |
| |
| register_extension("WGL_ARB_create_context"); |
| register_extension("WGL_ARB_create_context_profile"); |
| opengl_funcs.ext.p_wglCreateContextAttribsARB = macdrv_wglCreateContextAttribsARB; |
| |
| /* |
| * EXT Extensions |
| */ |
| register_extension("WGL_EXT_extensions_string"); |
| opengl_funcs.ext.p_wglGetExtensionsStringEXT = macdrv_wglGetExtensionsStringEXT; |
| |
| if (allow_vsync) |
| { |
| register_extension("WGL_EXT_swap_control"); |
| opengl_funcs.ext.p_wglSwapIntervalEXT = macdrv_wglSwapIntervalEXT; |
| opengl_funcs.ext.p_wglGetSwapIntervalEXT = macdrv_wglGetSwapIntervalEXT; |
| } |
| |
| /* Presumably identical to [W]GL_ARB_framebuffer_sRGB, above, but clients may |
| check for either, so register them separately. */ |
| if (gluCheckExtension((GLubyte*)"GL_EXT_framebuffer_sRGB", (GLubyte*)gl_info.glExtensions)) |
| register_extension("WGL_EXT_framebuffer_sRGB"); |
| |
| if (gluCheckExtension((GLubyte*)"GL_EXT_packed_float", (GLubyte*)gl_info.glExtensions)) |
| register_extension("WGL_EXT_pixel_format_packed_float"); |
| |
| /* |
| * WINE-specific WGL Extensions |
| */ |
| |
| /* In WineD3D we need the ability to set the pixel format more than once (e.g. after a device reset). |
| * The default wglSetPixelFormat doesn't allow this, so add our own which allows it. |
| */ |
| register_extension("WGL_WINE_pixel_format_passthrough"); |
| opengl_funcs.ext.p_wglSetPixelFormatWINE = macdrv_wglSetPixelFormatWINE; |
| } |
| |
| |
| static BOOL init_opengl(void) |
| { |
| static BOOL init_done = FALSE; |
| unsigned int i; |
| char buffer[200]; |
| |
| if (init_done) return (opengl_handle != NULL); |
| init_done = TRUE; |
| |
| TRACE("()\n"); |
| |
| dc_pbuffers = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); |
| if (!dc_pbuffers) |
| { |
| WARN("CFDictionaryCreateMutable failed\n"); |
| return FALSE; |
| } |
| |
| opengl_handle = wine_dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY|RTLD_LOCAL|RTLD_NOLOAD, buffer, sizeof(buffer)); |
| if (!opengl_handle) |
| { |
| ERR("Failed to load OpenGL: %s\n", buffer); |
| ERR("OpenGL support is disabled.\n"); |
| return FALSE; |
| } |
| |
| for (i = 0; i < sizeof(opengl_func_names)/sizeof(opengl_func_names[0]); i++) |
| { |
| if (!(((void **)&opengl_funcs.gl)[i] = wine_dlsym(opengl_handle, opengl_func_names[i], NULL, 0))) |
| { |
| ERR("%s not found in OpenGL, disabling.\n", opengl_func_names[i]); |
| goto failed; |
| } |
| } |
| |
| /* redirect some standard OpenGL functions */ |
| #define REDIRECT(func) \ |
| do { p##func = opengl_funcs.gl.p_##func; opengl_funcs.gl.p_##func = macdrv_##func; } while(0) |
| REDIRECT(glCopyPixels); |
| REDIRECT(glGetString); |
| REDIRECT(glReadPixels); |
| REDIRECT(glViewport); |
| if (skip_single_buffer_flushes || allow_vsync) |
| REDIRECT(glFlush); |
| if (allow_vsync) |
| REDIRECT(glFinish); |
| #undef REDIRECT |
| |
| /* redirect some OpenGL extension functions */ |
| #define REDIRECT(func) \ |
| do { if (opengl_funcs.ext.p_##func) { p##func = opengl_funcs.ext.p_##func; opengl_funcs.ext.p_##func = macdrv_##func; } } while(0) |
| REDIRECT(glCopyColorTable); |
| #undef REDIRECT |
| |
| if (!init_gl_info()) |
| goto failed; |
| |
| if (gluCheckExtension((GLubyte*)"GL_APPLE_flush_render", (GLubyte*)gl_info.glExtensions)) |
| pglFlushRenderAPPLE = wine_dlsym(opengl_handle, "glFlushRenderAPPLE", NULL, 0); |
| |
| load_extensions(); |
| if (!init_pixel_formats()) |
| goto failed; |
| |
| return TRUE; |
| |
| failed: |
| wine_dlclose(opengl_handle, NULL, 0); |
| opengl_handle = NULL; |
| return FALSE; |
| } |
| |
| |
| /*********************************************************************** |
| * sync_gl_view |
| * |
| * Synchronize the Mac GL view position with the Windows child window |
| * position. |
| */ |
| void sync_gl_view(struct macdrv_win_data *data) |
| { |
| RECT rect; |
| |
| TRACE("hwnd %p gl_view %p\n", data->hwnd, data->gl_view); |
| |
| if (!data->gl_view) return; |
| |
| if (get_gl_view_window_rect(data, NULL, &rect) && memcmp(&data->gl_rect, &rect, sizeof(rect))) |
| { |
| TRACE("Setting GL view %p frame to %s\n", data->gl_view, wine_dbgstr_rect(&rect)); |
| macdrv_set_view_window_and_frame(data->gl_view, NULL, cgrect_from_rect(rect)); |
| data->gl_rect = rect; |
| } |
| } |
| |
| |
| /********************************************************************** |
| * macdrv_wglDescribePixelFormat |
| */ |
| int macdrv_wglDescribePixelFormat(HDC hdc, int fmt, UINT size, PIXELFORMATDESCRIPTOR *descr) |
| { |
| const pixel_format *pf; |
| const struct color_mode *mode; |
| |
| TRACE("hdc %p fmt %d size %u descr %p\n", hdc, fmt, size, descr); |
| |
| if (!descr) return nb_displayable_formats; |
| if (size < sizeof(*descr)) return 0; |
| |
| if (!(pf = get_pixel_format(fmt, FALSE))) |
| return 0; |
| |
| memset(descr, 0, sizeof(*descr)); |
| descr->nSize = sizeof(*descr); |
| descr->nVersion = 1; |
| |
| descr->dwFlags = PFD_SUPPORT_OPENGL; |
| if (pf->window) descr->dwFlags |= PFD_DRAW_TO_WINDOW; |
| if (!pf->accelerated) descr->dwFlags |= PFD_GENERIC_FORMAT; |
| if (pf->double_buffer) descr->dwFlags |= PFD_DOUBLEBUFFER; |
| if (pf->stereo) descr->dwFlags |= PFD_STEREO; |
| if (pf->backing_store) descr->dwFlags |= PFD_SWAP_COPY; |
| |
| descr->iPixelType = PFD_TYPE_RGBA; |
| |
| mode = &color_modes[pf->color_mode]; |
| /* If the mode doesn't have alpha, return bits per pixel instead of color bits. |
| On Windows, color bits sometimes exceeds r+g+b (e.g. it's 32 for an |
| R8G8B8A0 pixel format). If an app depends on that and expects that |
| cColorBits >= 32 for such a pixel format, we need to accommodate that. */ |
| if (mode->alpha_bits) |
| descr->cColorBits = mode->color_bits; |
| else |
| descr->cColorBits = mode->bits_per_pixel; |
| descr->cRedBits = mode->red_bits; |
| descr->cRedShift = mode->red_shift; |
| descr->cGreenBits = mode->green_bits; |
| descr->cGreenShift = mode->green_shift; |
| descr->cBlueBits = mode->blue_bits; |
| descr->cBlueShift = mode->blue_shift; |
| descr->cAlphaBits = mode->alpha_bits; |
| descr->cAlphaShift = mode->alpha_shift; |
| |
| if (pf->accum_mode) |
| { |
| mode = &color_modes[pf->accum_mode - 1]; |
| descr->cAccumBits = mode->color_bits; |
| descr->cAccumRedBits = mode->red_bits; |
| descr->cAccumGreenBits = mode->green_bits; |
| descr->cAccumBlueBits = mode->blue_bits; |
| descr->cAccumAlphaBits = mode->alpha_bits; |
| } |
| |
| descr->cDepthBits = pf->depth_bits; |
| descr->cStencilBits = pf->stencil_bits; |
| descr->cAuxBuffers = pf->aux_buffers; |
| descr->iLayerType = PFD_MAIN_PLANE; |
| |
| TRACE("%s\n", debugstr_pf(pf)); |
| return nb_displayable_formats; |
| } |
| |
| /*********************************************************************** |
| * macdrv_wglCopyContext |
| */ |
| static BOOL macdrv_wglCopyContext(struct wgl_context *src, struct wgl_context *dst, UINT mask) |
| { |
| CGLError err; |
| |
| TRACE("src %p dst %p mask %x\n", src, dst, mask); |
| |
| err = CGLCopyContext(src->cglcontext, dst->cglcontext, mask); |
| if (err != kCGLNoError) |
| WARN("CGLCopyContext() failed with err %d %s\n", err, CGLErrorString(err)); |
| return (err == kCGLNoError); |
| } |
| |
| /*********************************************************************** |
| * macdrv_wglCreateContext |
| */ |
| static struct wgl_context *macdrv_wglCreateContext(HDC hdc) |
| { |
| struct wgl_context *context; |
| |
| TRACE("hdc %p\n", hdc); |
| |
| context = macdrv_wglCreateContextAttribsARB(hdc, NULL, NULL); |
| |
| return context; |
| } |
| |
| /*********************************************************************** |
| * macdrv_wglDeleteContext |
| */ |
| static void macdrv_wglDeleteContext(struct wgl_context *context) |
| { |
| TRACE("deleting context %p/%p/%p\n", context, context->context, context->cglcontext); |
| |
| EnterCriticalSection(&context_section); |
| list_remove(&context->entry); |
| LeaveCriticalSection(&context_section); |
| |
| macdrv_dispose_opengl_context(context->context); |
| HeapFree(GetProcessHeap(), 0, context); |
| } |
| |
| /*********************************************************************** |
| * macdrv_wglGetPixelFormat |
| */ |
| static int macdrv_wglGetPixelFormat(HDC hdc) |
| { |
| int format; |
| |
| format = get_dc_pixel_format(hdc); |
| |
| if (!is_valid_pixel_format(format)) /* not set yet */ |
| format = 0; |
| else if (!is_displayable_pixel_format(format)) |
| { |
| /* Non-displayable formats can't be used with traditional WGL calls. |
| * As has been verified on Windows GetPixelFormat doesn't fail but returns pixel format 1. */ |
| format = 1; |
| } |
| |
| TRACE(" hdc %p -> %d\n", hdc, format); |
| return format; |
| } |
| |
| /*********************************************************************** |
| * macdrv_wglGetProcAddress |
| */ |
| static PROC macdrv_wglGetProcAddress(const char *proc) |
| { |
| void *ret; |
| |
| if (!strncmp(proc, "wgl", 3)) return NULL; |
| ret = wine_dlsym(opengl_handle, proc, NULL, 0); |
| if (ret) |
| { |
| if (TRACE_ON(wgl)) |
| { |
| Dl_info info; |
| if (dladdr(ret, &info)) |
| TRACE("%s -> %s from %s\n", proc, info.dli_sname, info.dli_fname); |
| else |
| TRACE("%s -> %p (no library info)\n", proc, ret); |
| } |
| } |
| else |
| WARN("failed to find proc %s\n", debugstr_a(proc)); |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * macdrv_wglMakeCurrent |
| */ |
| static BOOL macdrv_wglMakeCurrent(HDC hdc, struct wgl_context *context) |
| { |
| TRACE("hdc %p context %p/%p/%p\n", hdc, context, (context ? context->context : NULL), |
| (context ? context->cglcontext : NULL)); |
| |
| return macdrv_wglMakeContextCurrentARB(hdc, hdc, context); |
| } |
| |
| /********************************************************************** |
| * macdrv_wglSetPixelFormat |
| */ |
| static BOOL macdrv_wglSetPixelFormat(HDC hdc, int fmt, const PIXELFORMATDESCRIPTOR *descr) |
| { |
| return set_pixel_format(hdc, fmt, FALSE); |
| } |
| |
| /*********************************************************************** |
| * macdrv_wglShareLists |
| */ |
| static BOOL macdrv_wglShareLists(struct wgl_context *org, struct wgl_context *dest) |
| { |
| macdrv_opengl_context saved_context; |
| CGLContextObj saved_cglcontext; |
| |
| TRACE("org %p dest %p\n", org, dest); |
| |
| /* Sharing of display lists works differently in Mac OpenGL and WGL. In Mac OpenGL it is done |
| * at context creation time but in case of WGL it is done using wglShareLists. |
| * |
| * The approach is to create a Mac OpenGL context in wglCreateContext / wglCreateContextAttribsARB |
| * and when a program requests sharing we recreate the destination context if it hasn't been made |
| * current or when it hasn't shared display lists before. |
| */ |
| |
| if (dest->has_been_current) |
| { |
| WARN("could not share display lists, the destination context has been current already\n"); |
| return FALSE; |
| } |
| else if (dest->sharing) |
| { |
| WARN("could not share display lists because dest has already shared lists before\n"); |
| return FALSE; |
| } |
| |
| /* Re-create the Mac context and share display lists */ |
| saved_context = dest->context; |
| saved_cglcontext = dest->cglcontext; |
| dest->context = NULL; |
| dest->cglcontext = NULL; |
| if (!create_context(dest, org->cglcontext, dest->major)) |
| { |
| dest->context = saved_context; |
| dest->cglcontext = saved_cglcontext; |
| return FALSE; |
| } |
| |
| /* Implicitly disposes of saved_cglcontext. */ |
| macdrv_dispose_opengl_context(saved_context); |
| |
| TRACE("re-created OpenGL context %p/%p/%p sharing lists with context %p/%p/%p\n", |
| dest, dest->context, dest->cglcontext, org, org->context, org->cglcontext); |
| |
| org->sharing = TRUE; |
| dest->sharing = TRUE; |
| |
| return TRUE; |
| } |
| |
| /********************************************************************** |
| * macdrv_wglSwapBuffers |
| */ |
| static BOOL macdrv_wglSwapBuffers(HDC hdc) |
| { |
| struct wgl_context *context = NtCurrentTeb()->glContext; |
| BOOL match = FALSE; |
| HWND hwnd; |
| |
| TRACE("hdc %p context %p/%p/%p\n", hdc, context, (context ? context->context : NULL), |
| (context ? context->cglcontext : NULL)); |
| |
| if (context) |
| sync_swap_interval(context); |
| |
| if ((hwnd = WindowFromDC(hdc))) |
| { |
| struct macdrv_win_data *data; |
| |
| if (!(data = get_win_data(hwnd))) |
| { |
| SetLastError(ERROR_INVALID_HANDLE); |
| return FALSE; |
| } |
| |
| if (context && context->draw_view == data->gl_view) |
| match = TRUE; |
| |
| release_win_data(data); |
| } |
| else |
| { |
| struct wgl_pbuffer *pbuffer; |
| |
| EnterCriticalSection(&dc_pbuffers_section); |
| pbuffer = (struct wgl_pbuffer*)CFDictionaryGetValue(dc_pbuffers, hdc); |
| LeaveCriticalSection(&dc_pbuffers_section); |
| |
| if (!pbuffer) |
| { |
| SetLastError(ERROR_INVALID_HANDLE); |
| return FALSE; |
| } |
| |
| if (context && context->draw_pbuffer == pbuffer) |
| match = TRUE; |
| } |
| |
| if (match) |
| macdrv_flush_opengl_context(context->context); |
| else |
| { |
| FIXME("current context %p doesn't match hdc %p; can't swap\n", context, hdc); |
| |
| /* If there is a current context, then wglSwapBuffers should do an implicit |
| glFlush(). That would be taken care of by macdrv_flush_opengl_context() |
| in the other branch, but we have to do it explicitly here. */ |
| if (context) |
| pglFlush(); |
| } |
| |
| return TRUE; |
| } |
| |
| static struct opengl_funcs opengl_funcs = |
| { |
| { |
| macdrv_wglCopyContext, /* p_wglCopyContext */ |
| macdrv_wglCreateContext, /* p_wglCreateContext */ |
| macdrv_wglDeleteContext, /* p_wglDeleteContext */ |
| macdrv_wglDescribePixelFormat, /* p_wglDescribePixelFormat */ |
| macdrv_wglGetPixelFormat, /* p_wglGetPixelFormat */ |
| macdrv_wglGetProcAddress, /* p_wglGetProcAddress */ |
| macdrv_wglMakeCurrent, /* p_wglMakeCurrent */ |
| macdrv_wglSetPixelFormat, /* p_wglSetPixelFormat */ |
| macdrv_wglShareLists, /* p_wglShareLists */ |
| macdrv_wglSwapBuffers, /* p_wglSwapBuffers */ |
| } |
| }; |
| |
| /********************************************************************** |
| * macdrv_wine_get_wgl_driver |
| */ |
| struct opengl_funcs *macdrv_wine_get_wgl_driver(PHYSDEV dev, UINT version) |
| { |
| if (version != WINE_WGL_DRIVER_VERSION) |
| { |
| ERR("version mismatch, opengl32 wants %u but macdrv has %u\n", version, WINE_WGL_DRIVER_VERSION); |
| return NULL; |
| } |
| |
| if (!init_opengl()) return (void *)-1; |
| |
| return &opengl_funcs; |
| } |