Skip to content

Commit ff003fb

Browse files
lexi-lambdamflatt
authored andcommitted
x11: Use the GLXFBConfig to set the visual of an OpenGL canvas
Without doing this, the visual will not necessarily match that required by GLX, which prevents certain features from working correctly, most notably multisampling.
1 parent 77c890f commit ff003fb

5 files changed

Lines changed: 130 additions & 68 deletions

File tree

gui-lib/mred/private/wx/gtk/gl-context.rkt

Lines changed: 84 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,6 @@
166166
;; GLX #defines/typedefs/enums
167167
(define _GLXFBConfig (_cpointer 'GLXFBConfig))
168168
(define _GLXContext (_cpointer/null 'GLXContext))
169-
(define _XVisualInfo (_cpointer 'XVisualInfo))
170169
;; Attribute tokens for glXGetConfig variants (all GLX versions):
171170
(define GLX_DOUBLEBUFFER 5)
172171
(define GLX_STEREO 6)
@@ -237,11 +236,11 @@
237236
(_fun _Display _GLXContext -> _bool))
238237

239238
(define-glx glXGetVisualFromFBConfig
240-
(_fun _Display _GLXFBConfig -> _XVisualInfo)
239+
(_fun _Display _GLXFBConfig -> _XVisualInfo-pointer)
241240
#:wrap (allocator XFree))
242241

243242
(define-glx glXCreateGLXPixmap
244-
(_fun _Display _XVisualInfo _XID -> _XID))
243+
(_fun _Display _XVisualInfo-pointer _XID -> _XID))
245244

246245
(define-glx glXDestroyGLXPixmap
247246
(_fun _Display _XID -> _void))
@@ -573,6 +572,67 @@
573572
(define-values (err value) (glXGetFBConfigAttrib xdisplay cfg attrib))
574573
(if (= err Success) value bad-value))
575574

575+
;; (or/c #f _GtkWidget) gl-config% boolean? -> (or/c _GLXFBConfig #f)
576+
(define (choose-glx-fbconfig widget conf wants-double?)
577+
(define glx-version (get-glx-version))
578+
579+
;; If widget isn't #f, use its display and screen
580+
(define display (gtk-maybe-widget-get-display widget))
581+
(define screen (gtk-maybe-widget-get-screen widget))
582+
583+
;; Get the X objects wrapped by the GDK objects
584+
(define xdisplay (gdk_x11_display_get_xdisplay display))
585+
(define xscreen (gdk_x11_screen_get_screen_number screen))
586+
587+
;; Create an attribute list using the GL config
588+
(define xattribs
589+
(append
590+
;; Be aware: we may get double buffering even if we don't ask for it
591+
(if wants-double?
592+
(if (send conf get-double-buffered) (list GLX_DOUBLEBUFFER True) null)
593+
null)
594+
(if (send conf get-stereo) (list GLX_STEREO True) null)
595+
;; Finish out with standard GLX 1.3 attributes
596+
(list
597+
GLX_X_RENDERABLE True ; yes, we want to use OpenGL to render today
598+
GLX_DEPTH_SIZE (send conf get-depth-size)
599+
GLX_STENCIL_SIZE (send conf get-stencil-size)
600+
GLX_ACCUM_RED_SIZE (send conf get-accum-size)
601+
GLX_ACCUM_GREEN_SIZE (send conf get-accum-size)
602+
GLX_ACCUM_BLUE_SIZE (send conf get-accum-size)
603+
GLX_ACCUM_ALPHA_SIZE (send conf get-accum-size)
604+
;; GLX_SAMPLES is handled below - GLX regards it as an absolute lower bound, which makes it
605+
;; too easy for user programs to fail to get a context
606+
None)))
607+
608+
(define multisample-size (send conf get-multisample-size))
609+
610+
;; Get all framebuffer configs for this display and screen that match the requested attributes,
611+
;; then sort them to put the best in front
612+
;; GLX already sorts them pretty well, so we just need a stable sort on multisamples at the moment
613+
(define cfgs
614+
(let* ([cfgs (cvector->list (glXChooseFBConfig xdisplay xscreen xattribs))]
615+
;; Keep all configs with multisample size <= requested (i.e. make multisample-size an
616+
;; abolute upper bound)
617+
[cfgs (if (< glx-version #e1.4)
618+
cfgs
619+
(filter (λ (cfg)
620+
(define m (glx-get-fbconfig-attrib xdisplay cfg GLX_SAMPLES 0))
621+
(<= m multisample-size))
622+
cfgs))]
623+
;; Sort all configs by multisample size, decreasing
624+
[cfgs (if (< glx-version #e1.4)
625+
cfgs
626+
(sort cfgs >
627+
#:key (λ (cfg) (glx-get-fbconfig-attrib xdisplay cfg GLX_SAMPLES 0))
628+
#:cache-keys? #t))])
629+
cfgs))
630+
631+
;; The framebuffer configs are sorted best-first, so choose the first
632+
(if (null? cfgs)
633+
#f
634+
(car cfgs)))
635+
576636
;; (or/c #f _GtkWidget) (or/c _GdkDrawable (is-a/c bitmap%) gl-config% boolean? -> gl-context%
577637
;; where X11 uses _GdkDrawable = (or/c _GtkWindow _GdkPixmap)
578638
;; and Wayland uses bitmap% that holds a Cairo ARGB32 image surface
@@ -711,68 +771,18 @@
711771
ctxt]
712772
[else
713773
(define glx-version (get-glx-version))
714-
715-
;; If widget isn't #f, use its display and screen
716774
(define display (gtk-maybe-widget-get-display widget))
717-
(define screen (gtk-maybe-widget-get-screen widget))
718-
719-
;; Get the X objects wrapped by the GDK objects
720775
(define xdisplay (gdk_x11_display_get_xdisplay display))
721-
(define xscreen (gdk_x11_screen_get_screen_number screen))
722-
723-
;; Create an attribute list using the GL config
724-
(define xattribs
725-
(append
726-
;; Be aware: we may get double buffering even if we don't ask for it
727-
(if wants-double?
728-
(if (send conf get-double-buffered) (list GLX_DOUBLEBUFFER True) null)
729-
null)
730-
(if (send conf get-stereo) (list GLX_STEREO True) null)
731-
;; Finish out with standard GLX 1.3 attributes
732-
(list
733-
GLX_X_RENDERABLE True ; yes, we want to use OpenGL to render today
734-
GLX_DEPTH_SIZE (send conf get-depth-size)
735-
GLX_STENCIL_SIZE (send conf get-stencil-size)
736-
GLX_ACCUM_RED_SIZE (send conf get-accum-size)
737-
GLX_ACCUM_GREEN_SIZE (send conf get-accum-size)
738-
GLX_ACCUM_BLUE_SIZE (send conf get-accum-size)
739-
GLX_ACCUM_ALPHA_SIZE (send conf get-accum-size)
740-
;; GLX_SAMPLES is handled below - GLX regards it as an absolute lower bound, which makes it
741-
;; too easy for user programs to fail to get a context
742-
None)))
743-
744-
(define multisample-size (send conf get-multisample-size))
745-
746-
;; Get all framebuffer configs for this display and screen that match the requested attributes,
747-
;; then sort them to put the best in front
748-
;; GLX already sorts them pretty well, so we just need a stable sort on multisamples at the moment
749-
(define cfgs
750-
(let* ([cfgs (cvector->list (glXChooseFBConfig xdisplay xscreen xattribs))]
751-
;; Keep all configs with multisample size <= requested (i.e. make multisample-size an
752-
;; abolute upper bound)
753-
[cfgs (if (< glx-version #e1.4)
754-
cfgs
755-
(filter (λ (cfg)
756-
(define m (glx-get-fbconfig-attrib xdisplay cfg GLX_SAMPLES 0))
757-
(<= m multisample-size))
758-
cfgs))]
759-
;; Sort all configs by multisample size, decreasing
760-
[cfgs (if (< glx-version #e1.4)
761-
cfgs
762-
(sort cfgs >
763-
#:key (λ (cfg) (glx-get-fbconfig-attrib xdisplay cfg GLX_SAMPLES 0))
764-
#:cache-keys? #t))])
765-
cfgs))
766-
776+
(define cfg (choose-glx-fbconfig widget conf wants-double?))
767777
(cond
768-
[(null? cfgs) #f]
769-
[else
770-
;; The framebuffer configs are sorted best-first, so choose the first
771-
(define cfg (car cfgs))
778+
[cfg
772779
(define share-gl
773780
(let ([share-ctxt (send conf get-share-context)])
774781
(and share-ctxt (send share-ctxt get-handle))))
775782

783+
(when (and widget (send conf get-sync-swap))
784+
(glXSwapIntervalEXT xdisplay (gdk_x11_drawable_get_xid drawable) 1))
785+
776786
;; Get a GL context
777787
(define gl
778788
(if (and (>= glx-version #e1.4)
@@ -788,9 +798,6 @@
788798
;; If it doesn't, the context will be version 1.4 or lower, unless GLX is implemented with
789799
;; proprietary extensions (NVIDIA's drivers sometimes do this)
790800

791-
(when (and widget (send conf get-sync-swap))
792-
(glXSwapIntervalEXT xdisplay (gdk_x11_drawable_get_xid drawable) 1))
793-
794801
;; Now wrap the GLX context in a gl-context%
795802
(cond
796803
[gl
@@ -820,7 +827,8 @@
820827
(unless (and gtk3? (not widget)) (g_object_unref drawable))
821828
(g_object_unref display)))
822829
ctxt]
823-
[else #f])])]))
830+
[else #f])]
831+
[else #f])]))
824832

825833
(define (make-gtk-widget-gl-context widget conf)
826834
(call-as-atomic
@@ -842,7 +850,20 @@
842850
(define widget-config-hash (make-weak-hasheq))
843851

844852
(define (prepare-widget-gl-context widget conf)
845-
(hash-set! widget-config-hash widget (if conf conf (make-object gl-config%))))
853+
(hash-set! widget-config-hash widget (if conf conf (make-object gl-config%)))
854+
855+
(when (and gtk_widget_set_visual (not wayland?))
856+
(cond
857+
[(and gtk_widget_get_realized
858+
(gtk_widget_get_realized widget))
859+
(log-warning "prepare-widget-gl-context: widget is already realized, cannot set visual from FBConfig")]
860+
[else
861+
(define cfg (choose-glx-fbconfig widget conf #t))
862+
(define display (gtk-maybe-widget-get-display widget))
863+
(define screen (gtk-maybe-widget-get-screen widget))
864+
(define xdisplay (gdk_x11_display_get_xdisplay display))
865+
(define xvisual-id (XVisualInfo-visual-id (glXGetVisualFromFBConfig xdisplay cfg)))
866+
(gtk_widget_set_visual widget (gdk_x11_screen_lookup_visual screen xvisual-id))])))
846867

847868
(define (create-widget-gl-context widget)
848869
(define conf (hash-ref widget-config-hash widget #f))

gui-lib/mred/private/wx/gtk/types.rkt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
_GtkWidget _GtkWindow
77
_GdkDisplay
88
_GdkScreen
9+
_GdkVisual
10+
_GdkPixmap
911
_gpointer
1012
_GType
1113
_GdkEventType
@@ -49,6 +51,8 @@
4951

5052
(define _GdkDisplay (_cpointer 'GdkDisplay))
5153
(define _GdkScreen (_cpointer 'GdkScreen))
54+
(define _GdkVisual (_cpointer 'GdkVisual))
55+
(define _GdkPixmap (_cpointer 'GdkPixmap))
5256

5357
(define _gpointer _GtkWidget)
5458

gui-lib/mred/private/wx/gtk/widget.rkt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#lang racket/base
22
(require ffi/unsafe
3+
ffi/unsafe/define
34
racket/class
45
"../../syntax.rkt"
56
"../../lock.rkt"
@@ -19,6 +20,8 @@
1920
gtk_widget_queue_draw
2021
gtk_widget_get_toplevel
2122
gtk_widget_translate_coordinates
23+
gtk_widget_get_realized
24+
gtk_widget_set_visual
2225

2326
gtk_vbox_new
2427
gtk_hbox_new
@@ -42,6 +45,10 @@
4245
(x : (_ptr o _int)) (y : (_ptr o _int))
4346
-> _gboolean
4447
-> (values x y)))
48+
(define-gtk gtk_widget_get_realized (_fun _GtkWidget -> _gboolean)
49+
#:fail (λ () #f))
50+
(define-gtk gtk_widget_set_visual (_fun _GtkWidget _GdkVisual -> _void)
51+
#:fail (λ () #f))
4552

4653
(define-signal-handler connect-destroy "destroy"
4754
(_fun _GtkWidget _pointer -> _void)

gui-lib/mred/private/wx/gtk/x11.rkt

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
(require ffi/unsafe
33
ffi/unsafe/define
44
ffi/unsafe/alloc
5+
"types.rkt"
56
"utils.rkt")
67

78
(provide
@@ -19,9 +20,14 @@
1920
gdk_x11_display_get_xdisplay
2021
gdk_x11_visual_get_xvisual
2122
gdk_x11_screen_get_screen_number
23+
gdk_x11_screen_lookup_visual
2224
gdk_x11_window_get_xid
2325

2426
_Display
27+
_Visual
28+
_XVisualInfo
29+
_XVisualInfo-pointer
30+
(struct-out XVisualInfo)
2531
_Window
2632
_Pixmap
2733
XCreatePixmap
@@ -35,15 +41,23 @@
3541
#:default-make-fail make-not-available)
3642

3743
(define _GdkDrawable _pointer)
38-
(define _GdkDisplay (_cpointer 'GdkDisplay))
39-
(define _GdkWindow (_cpointer 'GdkWindow))
40-
(define _GdkScreen (_cpointer 'GdkScreen))
41-
(define _GdkVisual (_cpointer 'GdkVisual))
42-
(define _GdkPixmap (_cpointer 'GdkPixmap))
4344
(define _Visual (_cpointer 'Visual))
45+
(define _VisualID _ulong)
4446
(define _Display (_cpointer 'Display))
4547
(define _Drawable _ulong)
4648

49+
(define-cstruct _XVisualInfo
50+
([visual _Visual]
51+
[visual-id _VisualID]
52+
[screen _int]
53+
[depth _int]
54+
[class _int]
55+
[red-mask _ulong]
56+
[green-mask _ulong]
57+
[blue-mask _ulong]
58+
[colormap-size _int]
59+
[bits-per-rgb _int]))
60+
4761
;; This should be `_ulong`, but we use pointers for various
4862
;; reasons, including support for dealloctaors:
4963
(define _Window (_cpointer 'Window))
@@ -88,6 +102,9 @@
88102
(define-gdk gdk_x11_screen_get_screen_number (_fun _GdkScreen -> _int)
89103
#:make-fail make-not-available)
90104

105+
(define-gdk gdk_x11_screen_lookup_visual (_fun _GdkScreen _VisualID -> _GdkVisual)
106+
#:make-fail make-not-available)
107+
91108
(define-x11 XFreePixmap (_fun _Display _Pixmap -> _void)
92109
#:wrap (deallocator cadr))
93110
(define-x11 XCreatePixmap (_fun _Display _Window _int _int _int -> _Pixmap)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#lang racket/gui
2+
(require ffi/cvector
3+
sgl/gl
4+
rackunit)
5+
6+
(define gl-config (new gl-config%))
7+
(send gl-config set-multisample-size 4)
8+
9+
(define f (new frame% [label ""] [width 100] [height 300]))
10+
(define c (new canvas% [parent f] [style '(gl no-autoclear)] [gl-config gl-config]))
11+
(send c with-gl-context
12+
(λ ()
13+
(check-equal? (cvector-ref (glGetIntegerv GL_SAMPLE_BUFFERS 1) 0) 1)))

0 commit comments

Comments
 (0)